// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.


using NuGet.Common;
using NuGet.Frameworks;
using NuGet.ProjectModel;

namespace Microsoft.NET.Build.Tests
{
    public class GivenThereAreDefaultItems : SdkTest
    {
        public GivenThereAreDefaultItems(ITestOutputHelper log) : base(log)
        {
        }

        [Fact]
        public void It_ignores_excluded_folders()
        {
            Action<GetValuesCommand> setup = getValuesCommand =>
            {
                foreach (string folder in new[] { "bin", "obj", ".vscode" })
                {
                    WriteFile(Path.Combine(getValuesCommand.ProjectRootPath, folder, "source.cs"),
                        "!InvalidCSharp!");
                }

                WriteFile(Path.Combine(getValuesCommand.ProjectRootPath, "Code", "Class1.cs"),
                    "public class Class1 {}");

                WriteFile(Path.Combine(getValuesCommand.ProjectRootPath, "Packages", "Package.cs"),
                    "public class Package {}");
            };

            var compileItems = GivenThatWeWantToBuildALibrary.GetValuesFromTestLibrary(Log, _testAssetsManager, "Compile", setup);

            RemoveGeneratedCompileItems(compileItems);

            var expectedItems = new[]
            {
                "Helper.cs",
                @"Code\Class1.cs",
                @"Packages\Package.cs"
            }
            .Select(item => item.Replace('\\', Path.DirectorySeparatorChar))
            .ToArray();

            compileItems.Should().BeEquivalentTo(expectedItems);
        }

        [Fact]
        public void It_excludes_items_in_a_custom_outputpath()
        {
            Action<GetValuesCommand> setup = getValuesCommand =>
            {

                WriteFile(Path.Combine(getValuesCommand.ProjectRootPath, "Output", "CSharpInOutput.cs"),
                    "!InvalidCSharp!");

                WriteFile(Path.Combine(getValuesCommand.ProjectRootPath, "Code", "Class1.cs"),
                    "public class Class1 {}");
            };

            Action<XDocument> projectChanges = project =>
            {
                var ns = project.Root.Name.Namespace;

                var propertyGroup = new XElement(ns + "PropertyGroup");
                project.Root.Add(propertyGroup);
                propertyGroup.Add(new XElement(ns + "OutputPath", "Output"));
            };


            var compileItems = GivenThatWeWantToBuildALibrary.GetValuesFromTestLibrary(Log, _testAssetsManager, "Compile", setup, projectChanges: projectChanges);

            RemoveGeneratedCompileItems(compileItems);

            var expectedItems = new[]
            {
                "Helper.cs",
                @"Code\Class1.cs"
            }
            .Select(item => item.Replace('\\', Path.DirectorySeparatorChar))
            .ToArray();

            compileItems.Should().BeEquivalentTo(expectedItems);
        }

        [Fact]
        public void It_allows_excluded_folders_to_be_overridden()
        {
            Action<GetValuesCommand> setup = getValuesCommand =>
            {
                foreach (string folder in new[] { "bin", "obj", "packages" })
                {
                    WriteFile(Path.Combine(getValuesCommand.ProjectRootPath, folder, "source.cs"),
                        $"public class ClassFrom_{folder} {{}}");
                }

                WriteFile(Path.Combine(getValuesCommand.ProjectRootPath, "Code", "Class1.cs"),
                    "public class Class1 {}");
            };

            Action<XDocument> projectChanges = project =>
            {
                var ns = project.Root.Name.Namespace;

                project.Root.Element(ns + "PropertyGroup").Add(new XElement(ns + "EnableDefaultItems", "False"));

                XElement itemGroup = new(ns + "ItemGroup");
                project.Root.Add(itemGroup);
                itemGroup.Add(new XElement(ns + "Compile", new XAttribute("Include", "**\\*.cs")));
            };

            var compileItems = GivenThatWeWantToBuildALibrary.GetValuesFromTestLibrary(Log, _testAssetsManager,
                "Compile", setup, new[] { "/p:DisableDefaultRemoves=true" }, GetValuesCommand.ValueType.Item,
                projectChanges: projectChanges);

            RemoveGeneratedCompileItems(compileItems);

            var expectedItems = new[]
            {
                "Helper.cs",
                @"Code\Class1.cs",
                @"bin\source.cs",
                @"obj\source.cs",
                @"packages\source.cs"
            }
            .Select(item => item.Replace('\\', Path.DirectorySeparatorChar))
            .ToArray();

            compileItems.Should().BeEquivalentTo(expectedItems);
        }

        [Fact]
        public void It_allows_items_outside_project_root_to_be_included()
        {
            Action<GetValuesCommand> setup = getValuesCommand =>
            {
                string sharedCodePath = Path.Combine(Path.GetDirectoryName(getValuesCommand.ProjectRootPath), "Shared");
                WriteFile(Path.Combine(sharedCodePath, "Shared.cs"),
                    "public class SharedClass {}");

                WriteFile(Path.Combine(getValuesCommand.ProjectRootPath, "Code", "Class1.cs"),
                    "public class Class1 {}");
            };

            Action<XDocument> projectChanges = project =>
            {
                var ns = project.Root.Name.Namespace;

                XElement itemGroup = new(ns + "ItemGroup");
                project.Root.Add(itemGroup);
                itemGroup.Add(new XElement(ns + "Compile", new XAttribute("Include", "..\\Shared\\**\\*.cs")));
            };

            var compileItems = GivenThatWeWantToBuildALibrary.GetValuesFromTestLibrary(Log, _testAssetsManager, "Compile", setup, projectChanges: projectChanges);

            RemoveGeneratedCompileItems(compileItems);

            var expectedItems = new[]
            {
                "Helper.cs",
                @"Code\Class1.cs",
                @"..\Shared\Shared.cs",
            }
            .Select(item => item.Replace('\\', Path.DirectorySeparatorChar))
            .ToArray();

            compileItems.Should().BeEquivalentTo(expectedItems);
        }

        [Fact]
        public void It_allows_a_project_subfolder_to_be_excluded()
        {
            Action<GetValuesCommand> setup = getValuesCommand =>
            {
                WriteFile(Path.Combine(getValuesCommand.ProjectRootPath, "Code", "Class1.cs"),
                    "public class Class1 {}");
                WriteFile(Path.Combine(getValuesCommand.ProjectRootPath, "Excluded", "Excluded.cs"),
                    "!InvalidCSharp!");
            };

            Action<XDocument> projectChanges = project =>
            {
                var ns = project.Root.Name.Namespace;

                XElement itemGroup = new(ns + "ItemGroup");
                project.Root.Add(itemGroup);
                itemGroup.Add(new XElement(ns + "Compile", new XAttribute("Remove", "Excluded\\**\\*.cs")));
            };

            var compileItems = GivenThatWeWantToBuildALibrary.GetValuesFromTestLibrary(Log, _testAssetsManager, "Compile", setup, projectChanges: projectChanges);

            RemoveGeneratedCompileItems(compileItems);

            var expectedItems = new[]
            {
                "Helper.cs",
                @"Code\Class1.cs",
            }
            .Select(item => item.Replace('\\', Path.DirectorySeparatorChar))
            .ToArray();

            compileItems.Should().BeEquivalentTo(expectedItems);
        }

        [Fact]
        public void It_allows_files_in_the_obj_folder_to_be_explicitly_included()
        {
            Action<GetValuesCommand> setup = getValuesCommand =>
            {
                WriteFile(Path.Combine(getValuesCommand.ProjectRootPath, "Code", "Class1.cs"),
                    "public class Class1 {}");
                WriteFile(Path.Combine(getValuesCommand.ProjectRootPath, "obj", "Class2.cs"),
                    "public class Class2 {}");
                WriteFile(Path.Combine(getValuesCommand.ProjectRootPath, "obj", "Excluded.cs"),
                    "!InvalidCSharp!");
            };

            Action<XDocument> projectChanges = project =>
            {
                var ns = project.Root.Name.Namespace;

                XElement itemGroup = new(ns + "ItemGroup");
                project.Root.Add(itemGroup);
                itemGroup.Add(new XElement(ns + "Compile", new XAttribute("Include", "obj\\Class2.cs")));
            };

            var compileItems = GivenThatWeWantToBuildALibrary.GetValuesFromTestLibrary(Log, _testAssetsManager, "Compile", setup, projectChanges: projectChanges);

            RemoveGeneratedCompileItems(compileItems);

            var expectedItems = new[]
            {
                "Helper.cs",
                @"Code\Class1.cs",
                @"obj\Class2.cs"
            }
            .Select(item => item.Replace('\\', Path.DirectorySeparatorChar))
            .ToArray();

            compileItems.Should().BeEquivalentTo(expectedItems);
        }

        [Fact]
        public void It_allows_a_CSharp_file_to_be_used_as_an_EmbeddedResource()
        {
            Action<GetValuesCommand> setup = getValuesCommand =>
            {
                WriteFile(Path.Combine(getValuesCommand.ProjectRootPath, "Code", "Class1.cs"),
                    "public class Class1 {}");
                WriteFile(Path.Combine(getValuesCommand.ProjectRootPath, "CSharpAsResource.cs"),
                    "public class CSharpAsResource {}");
            };

            Action<XDocument> projectChanges = project =>
            {
                var ns = project.Root.Name.Namespace;

                XElement itemGroup = new(ns + "ItemGroup");
                project.Root.Add(itemGroup);
                itemGroup.Add(new XElement(ns + "EmbeddedResource", new XAttribute("Include", "CSharpAsResource.cs")));
                itemGroup.Add(new XElement(ns + "Compile", new XAttribute("Remove", "CSharpAsResource.cs")));
            };

            var compileItems = GivenThatWeWantToBuildALibrary.GetValuesFromTestLibrary(Log, _testAssetsManager, "Compile", setup, projectChanges: projectChanges);

            RemoveGeneratedCompileItems(compileItems);

            var expectedItems = new[]
            {
                "Helper.cs",
                @"Code\Class1.cs",
            }
            .Select(item => item.Replace('\\', Path.DirectorySeparatorChar))
            .ToArray();

            compileItems.Should().BeEquivalentTo(expectedItems);


            var embeddedResourceItems = GivenThatWeWantToBuildALibrary.GetValuesFromTestLibrary(Log, _testAssetsManager, "EmbeddedResource", setup, projectChanges: projectChanges, identifier: "EmbeddedResource");

            var expectedEmbeddedResourceItems = new[]
            {
                "CSharpAsResource.cs"
            }
            .Select(item => item.Replace('\\', Path.DirectorySeparatorChar))
            .ToArray();

            embeddedResourceItems.Should().BeEquivalentTo(expectedEmbeddedResourceItems);
        }

        [Fact]
        public void It_allows_a_CSharp_file_to_be_used_as_Content()
        {
            Action<GetValuesCommand> setup = getValuesCommand =>
            {
                WriteFile(Path.Combine(getValuesCommand.ProjectRootPath, "Code", "Class1.cs"),
                    "public class Class1 {}");
                WriteFile(Path.Combine(getValuesCommand.ProjectRootPath, "CSharpAsContent.cs"),
                    "public class CSharpAsContent {}");

                WriteFile(Path.Combine(getValuesCommand.ProjectRootPath, "None.txt"), "Content file");
            };

            Action<XDocument> projectChanges = project =>
            {
                var ns = project.Root.Name.Namespace;

                XElement itemGroup = new(ns + "ItemGroup");
                project.Root.Add(itemGroup);
                itemGroup.Add(new XElement(ns + "Content", new XAttribute("Include", "CSharpAsContent.cs"),
                    new XAttribute("CopyToOutputDirectory", "true")));
                itemGroup.Add(new XElement(ns + "Compile", new XAttribute("Remove", "CSharpAsContent.cs")));
            };

            var compileItems = GivenThatWeWantToBuildALibrary.GetValuesFromTestLibrary(Log, _testAssetsManager, "Compile", setup, projectChanges: projectChanges);

            RemoveGeneratedCompileItems(compileItems);

            var expectedItems = new[]
            {
                "Helper.cs",
                @"Code\Class1.cs",
            }
            .Select(item => item.Replace('\\', Path.DirectorySeparatorChar))
            .ToArray();

            compileItems.Should().BeEquivalentTo(expectedItems);


            var contentItems = GivenThatWeWantToBuildALibrary.GetValuesFromTestLibrary(Log, _testAssetsManager, "Content", setup, projectChanges: projectChanges, identifier: "Content");

            var expectedContentItems = new[]
            {
                "CSharpAsContent.cs",
            }
            .Select(item => item.Replace('\\', Path.DirectorySeparatorChar))
            .ToArray();

            contentItems.Should().BeEquivalentTo(expectedContentItems);

            var noneItems = GivenThatWeWantToBuildALibrary.GetValuesFromTestLibrary(Log, _testAssetsManager, "None", setup, projectChanges: projectChanges, identifier: expectedContentItems.GetHashCode().ToString());

            var expectedNoneItems = new[]
            {
                "None.txt"
            }
            .Select(item => item.Replace('\\', Path.DirectorySeparatorChar))
            .ToArray();

            noneItems.Should().BeEquivalentTo(expectedNoneItems);
        }

        [Fact]
        public void It_does_not_include_items_in_any_group_if_group_specific_default_include_properties_are_false()
        {
            var testProject = new TestProject()
            {
                Name = "DontIncludeSourceFilesInNone",
                TargetFrameworks = ToolsetInfo.CurrentTargetFramework,
                IsExe = true,
            };
            testProject.AdditionalProperties["EnableDefaultCompileItems"] = "false";
            testProject.AdditionalProperties["EnableDefaultResourceItems"] = "false";

            // Windows App SDK related
            testProject.AdditionalProperties["EnableDefaultWindowsAppSdkContentItems"] = "true";
            testProject.AdditionalProperties["EnableDefaultWindowsAppSdkPRIResourceItems"] = "true";
            testProject.AdditionalProperties["EnableDefaultContentItems"] = "false";
            testProject.AdditionalProperties["EnableDefaultPRIResourceItems"] = "false";

            var testAsset = _testAssetsManager.CreateTestProject(testProject)
                .WithProjectChanges(project =>
                {
                    // "Manual" include via project file modification.
                    var ns = project.Root.Name.Namespace;
                    XElement itemGroup = new(ns + "ItemGroup");
                    project.Root.Add(itemGroup);
                    itemGroup.Add(new XElement(ns + "Compile", new XAttribute("Include", testProject.Name + ".cs")));
                    itemGroup.Add(new XElement(ns + "Compile", new XAttribute("Include", testProject.Name + "Program.cs")));
                });

            var projectFolder = Path.Combine(testAsset.TestRoot, testProject.Name);

            File.WriteAllText(Path.Combine(projectFolder, "ShouldBeIgnored.cs"), "!InvalidCSharp!");
            File.WriteAllText(Path.Combine(projectFolder, "Resources.resx"), "<Resource/>");
            File.WriteAllText(Path.Combine(projectFolder, "ResourcesResw.resw"), "<root/>");
            File.WriteAllText(Path.Combine(projectFolder, "TestImage.jpg"), "");

            // Validate Compile items.
            var getCompileItemsCommand = new GetValuesCommand(Log, projectFolder, testProject.TargetFrameworks, "Compile", GetValuesCommand.ValueType.Item);
            getCompileItemsCommand.Execute()
                .Should()
                .Pass();

            var compileItems = getCompileItemsCommand.GetValues();
            RemoveGeneratedCompileItems(compileItems);
            compileItems.Should().BeEquivalentTo(new[] { testProject.Name + ".cs", testProject.Name + "Program.cs" });

            // Validate None items.
            var getNoneItemsCommand = new GetValuesCommand(Log, projectFolder, testProject.TargetFrameworks, "None", GetValuesCommand.ValueType.Item);
            getNoneItemsCommand.Execute()
                .Should()
                .Pass();

            getNoneItemsCommand.GetValues()
                .Should().BeEmpty();

            // Validate Resource items.
            var getResourceItemsCommand = new GetValuesCommand(Log, projectFolder, testProject.TargetFrameworks, "Resource", GetValuesCommand.ValueType.Item);
            getResourceItemsCommand.Execute()
                .Should()
                .Pass();

            getResourceItemsCommand.GetValues()
                .Should().BeEmpty();

            // Validate PRIResource items.
            var getPRIResourceItemsCommand = new GetValuesCommand(Log, projectFolder, testProject.TargetFrameworks, "PRIResource", GetValuesCommand.ValueType.Item);
            getPRIResourceItemsCommand.Execute()
                .Should()
                .Pass();

            getPRIResourceItemsCommand.GetValues()
                .Should().BeEmpty();

            // Validate Content items.
            var getContentItemsCommand = new GetValuesCommand(Log, projectFolder, testProject.TargetFrameworks, "Content", GetValuesCommand.ValueType.Item);
            getContentItemsCommand.Execute()
                .Should()
                .Pass();

            getContentItemsCommand.GetValues()
                .Should().BeEmpty();
        }

        [Fact]
        public void Default_items_have_the_correct_relative_paths()
        {
            Action<XDocument> projectChanges = project =>
            {
                var ns = project.Root.Name.Namespace;

                var propertyGroup = new XElement(ns + "PropertyGroup");
                project.Root.Add(propertyGroup);
                propertyGroup.Add(new XElement(ns + "EnableDefaultContentItems", "false"));

                //  Copy all None items to output directory
                var itemGroup = new XElement(ns + "ItemGroup");
                project.Root.Add(itemGroup);
                itemGroup.Add(new XElement(ns + "None", new XAttribute("Update", "@(None)"), new XAttribute("CopyToOutputDirectory", "PreserveNewest")));
            };

            var testAsset = _testAssetsManager
                .CopyTestAsset("AppWithLibrary")
                .WithSource()
                .WithProjectChanges(projectChanges);

            var buildCommand = new BuildCommand(testAsset, "TestLibrary");

            WriteFile(Path.Combine(buildCommand.ProjectRootPath, "ProjectRoot.txt"), "ProjectRoot");
            WriteFile(Path.Combine(buildCommand.ProjectRootPath, "Subfolder", "ProjectSubfolder.txt"), "ProjectSubfolder");
            WriteFile(Path.Combine(buildCommand.ProjectRootPath, "wwwroot", "wwwroot.txt"), "wwwroot");
            WriteFile(Path.Combine(buildCommand.ProjectRootPath, "wwwroot", "wwwsubfolder", "wwwsubfolder.txt"), "wwwsubfolder");

            buildCommand
                .Execute()
                .Should()
                .Pass();

            var outputDirectory = buildCommand.GetOutputDirectory("netstandard1.5");

            outputDirectory.Should().OnlyHaveFiles(new[] {
                "TestLibrary.dll",
                "TestLibrary.pdb",
                "TestLibrary.deps.json",
                "ProjectRoot.txt",
                "Subfolder/ProjectSubfolder.txt",
                "wwwroot/wwwroot.txt",
                "wwwroot/wwwsubfolder/wwwsubfolder.txt",
            });
        }

        [RequiresMSBuildVersionFact("17.1.0.60101")]
        public void Compile_items_can_be_explicitly_specified_while_default_EmbeddedResource_items_are_used()
        {
            Action<XDocument> projectChanges = project =>
            {
                var ns = project.Root.Name.Namespace;

                project.Root.Element(ns + "PropertyGroup").Add(
                    new XElement(ns + "EnableDefaultCompileItems", "false"));

                var itemGroup = new XElement(ns + "ItemGroup");
                project.Root.Add(itemGroup);
                itemGroup.Add(new XElement(ns + "Compile", new XAttribute("Include", "Program.cs")));
            };

            Action<BuildCommand> setup = buildCommand =>
            {
                WriteFile(Path.Combine(buildCommand.ProjectRootPath, "ShouldNotBeCompiled.cs"),
                    "!InvalidCSharp!");
            };

            GivenThatWeWantAllResourcesInSatellite.TestSatelliteResources(Log, _testAssetsManager, projectChanges, setup, "ExplicitCompileDefaultEmbeddedResource");
        }

        [Fact]
        public void It_gives_an_error_message_if_duplicate_compile_items_are_included()
        {
            var testProject = new TestProject()
            {
                Name = "DuplicateCompileItems",
                TargetFrameworks = "netstandard1.6",
            };

            var testAsset = _testAssetsManager.CreateTestProject(testProject)
                .WithProjectChanges(project =>
                {
                    var ns = project.Root.Name.Namespace;
                    var itemGroup = new XElement(ns + "ItemGroup");
                    project.Root.Add(itemGroup);
                    itemGroup.Add(new XElement(ns + "Compile", new XAttribute("Include", @"**\*.cs")));
                });

            var buildCommand = new BuildCommand(testAsset);

            WriteFile(Path.Combine(buildCommand.ProjectRootPath, "Class1.cs"), "public class Class1 {}");

            buildCommand
                .Execute()
                .Should()
                .Fail()
                .And.HaveStdOutContaining("DuplicateCompileItems.cs")
                .And.HaveStdOutContaining("Class1.cs")
                .And.HaveStdOutContaining("EnableDefaultCompileItems");
        }

        [Fact]
        public void Implicit_package_references_are_overridden_by_PackageReference_includes_in_the_project_file()
        {
            var testProject = new TestProject()
            {
                //  Underscore is in the project name so we can verify that the warning message output contained "PackageReference"
                Name = "DeduplicatePackage_Reference",
                TargetFrameworks = "netstandard1.6",
            };

            var testAsset = _testAssetsManager.CreateTestProject(testProject, "DeduplicatePackage_Reference")
                .WithProjectChanges(project =>
                {
                    var ns = project.Root.Name.Namespace;

                    //  Set the implicit package reference version to something that doesn't exist to verify that the Include
                    //  in the project file overrides the implicit one
                    project.Root.Element(ns + "PropertyGroup").Add(
                        new XElement(ns + "NetStandardImplicitPackageVersion", "0.1.0-does-not-exist"));

                    var itemGroup = new XElement(ns + "ItemGroup");
                    project.Root.Add(itemGroup);

                    //  Use non-standard casing for the explicit package reference to verify that comparison is case-insensitive
                    itemGroup.Add(new XElement(ns + "PackageReference",
                        new XAttribute("Include", "netstandard.Library"), new XAttribute("Version", "1.6.1")));
                });

            var buildCommand = new BuildCommand(testAsset);

            buildCommand
                .Execute()
                .Should()
                .Pass()
                .And.HaveStdOutContaining("PackageReference")
                .And.HaveStdOutContaining("'NETStandard.Library'");
        }

        [Fact]
        public void ImplicitFrameworkReferencesAreOverriddenByProjectFile()
        {
            var testProject = new TestProject()
            {
                Name = "OverrideImplicitFrameworkReference",
                TargetFrameworks = ToolsetInfo.CurrentTargetFramework,
            };

            var testAsset = _testAssetsManager.CreateTestProject(testProject)
                .WithProjectChanges(project =>
                {
                    var ns = project.Root.Name.Namespace;

                    var itemGroup = new XElement(ns + "ItemGroup");
                    project.Root.Add(itemGroup);

                    itemGroup.Add(new XElement(ns + "FrameworkReference",
                        new XAttribute("Include", "Microsoft.NETCore.App")));
                });

            var restoreCommand = new RestoreCommand(testAsset);

            restoreCommand
                .Execute()
                .Should()
                .Pass()
                .And.HaveStdOutContaining("NETSDK1086");

            var buildCommand = new BuildCommand(testAsset);

            buildCommand
                .Execute()
                .Should()
                .Pass()
                .And.HaveStdOutContaining("NETSDK1086");
        }

        [Fact]
        public void DuplicateFrameworkReferencesCauseError()
        {
            var testProject = new TestProject()
            {
                Name = "DuplicateFrameworkReference",
                TargetFrameworks = ToolsetInfo.CurrentTargetFramework,
            };

            var testAsset = _testAssetsManager.CreateTestProject(testProject)
                .WithProjectChanges(project =>
                {
                    var ns = project.Root.Name.Namespace;

                    project.Root.Element(ns + "PropertyGroup").Add(
                        new XElement(ns + "DisableImplicitFrameworkReferences", "true"));

                    var itemGroup = new XElement(ns + "ItemGroup");
                    project.Root.Add(itemGroup);

                    itemGroup.Add(new XElement(ns + "FrameworkReference",
                        new XAttribute("Include", "Microsoft.NETCore.App")));

                    itemGroup.Add(new XElement(ns + "FrameworkReference",
                        new XAttribute("Include", "Microsoft.NETCore.App")));
                });

            var restoreCommand = new RestoreCommand(testAsset);

            restoreCommand
                .Execute()
                .Should()
                .Fail()
                .And.HaveStdOutContaining("NETSDK1087");
        }

        [Theory]
        [InlineData(false)]
        [InlineData(true)]
        public void Implicit_NetCoreApp_reference_can_be_overridden(bool disableImplicitFrameworkReferences)
        {
            var testProject = new TestProject()
            {
                Name = "OverrideNetCoreApp",
                TargetFrameworks = "netcoreapp2.0",
                IsExe = true
            };

            if (disableImplicitFrameworkReferences)
            {
                testProject.AdditionalProperties["DisableImplicitFrameworkReferences"] = "true";
            }

            string explicitPackageVersion = "2.0.3";
            testProject.PackageReferences.Add(new TestPackageReference("Microsoft.NETCore.App", explicitPackageVersion));

            var testAsset = _testAssetsManager.CreateTestProject(testProject, identifier: disableImplicitFrameworkReferences.ToString());

            var buildCommand = new BuildCommand(testAsset);

            buildCommand
                .Execute()
                .Should()
                .Pass()
                .And
                .NotHaveStdOutContaining("NETSDK1071");
            ;

            LockFile lockFile = LockFileUtilities.GetLockFile(Path.Combine(buildCommand.ProjectRootPath, "obj", "project.assets.json"), NullLogger.Instance);

            var target = lockFile.GetTarget(NuGetFramework.Parse(testProject.TargetFrameworks), null);
            var netCoreAppLibrary = target.Libraries.Single(l => l.Name == "Microsoft.NETCore.App");
            netCoreAppLibrary.Version.ToString().Should().Be(explicitPackageVersion);
        }

        [Fact]
        public void DuplicatePackageReferencesCanBeUsed()
        {
            var testProject = new TestProject()
            {
                Name = "DuplicatePackageReference",
                TargetFrameworks = ToolsetInfo.CurrentTargetFramework,
            };

            testProject.PackageReferences.Add(new TestPackageReference("Newtonsoft.Json", ToolsetInfo.GetNewtonsoftJsonPackageVersion()));
            testProject.PackageReferences.Add(new TestPackageReference("Newtonsoft.Json", ToolsetInfo.GetNewtonsoftJsonPackageVersion()));

            testProject.SourceFiles["Test.cs"] = @"
public class Class1
{
    public static void Test()
    {
        Newtonsoft.Json.Linq.JToken.Parse(""{ }"");
    }
}";

            var testAsset = _testAssetsManager.CreateTestProject(testProject);

            var buildCommand = new BuildCommand(testAsset);

            buildCommand
                .Execute()
                .Should()
                .Pass();

            //  https://github.com/dotnet/sdk/issues/3027 could cause a situation where the build fails in VS
            //  but not the command line, apparently due to differences in how the different restores handle
            //  duplicate package references.  So for this test, check the metadata.

            var getValuesCommand = new GetValuesCommand(Log, Path.Combine(testAsset.TestRoot, testProject.Name),
                testProject.TargetFrameworks, "PackageReference", GetValuesCommand.ValueType.Item);

            getValuesCommand.MetadataNames.Add("PrivateAssets");
            getValuesCommand.MetadataNames.Add("ExcludeAssets");
            getValuesCommand.MetadataNames.Add("IsImplicitlyDefined");

            getValuesCommand.Execute().Should().Pass();

            var packageReferences = getValuesCommand.GetValuesWithMetadata();

            var newtonsoftReferences = packageReferences.Where(pr => pr.value == "Newtonsoft.Json");

            newtonsoftReferences.Count().Should().BeGreaterOrEqualTo(1);

            foreach (var r in newtonsoftReferences)
            {
                r.metadata["PrivateAssets"].Should().BeEmpty();
                r.metadata["ExcludeAssets"].Should().BeEmpty();
                r.metadata["IsImplicitlyDefined"].Should().BeEmpty();
            }
        }

        [Fact]
        public void It_includes_Windows_App_SDK_items_in_the_correct_groups_if_Windows_App_SDK_is_present()
        {
            var testProject = new TestProject()
            {
                Name = "DontIncludeSourceFilesInNone",
                TargetFrameworks = ToolsetInfo.CurrentTargetFramework,
                IsExe = true,
            };

            // Windows App SDK
            testProject.AdditionalProperties["EnableDefaultWindowsAppSdkContentItems"] = "true";
            testProject.AdditionalProperties["EnableDefaultWindowsAppSdkPRIResourceItems"] = "true";

            var testAsset = _testAssetsManager.CreateTestProject(testProject);
            var projectFolder = Path.Combine(testAsset.TestRoot, testProject.Name);

            File.WriteAllText(Path.Combine(projectFolder, "ResourcesResw.resw"), "<root/>");
            string[] imageFiles = { "TestImage1.png", "TestImage2.bmp", "TestImage3.jpg", "TestImage4.dds", "TestImage5.tif", "TestImage6.tga", "TestImage7.gif" };
            foreach (string fileName in imageFiles)
            {
                File.WriteAllText(Path.Combine(projectFolder, fileName), "");
            }

            // Validate None items.
            var getNoneItemsCommand = new GetValuesCommand(Log, projectFolder, testProject.TargetFrameworks, "None", GetValuesCommand.ValueType.Item);
            getNoneItemsCommand.Execute()
                .Should()
                .Pass();

            getNoneItemsCommand.GetValues()
                .Should()
                .BeEmpty();

            // Validate PRIResource items.
            var getPRIResourceItemsCommand = new GetValuesCommand(Log, projectFolder, testProject.TargetFrameworks, "PRIResource", GetValuesCommand.ValueType.Item);
            getPRIResourceItemsCommand.Execute()
                .Should()
                .Pass();

            var getPRIResourceItems = getPRIResourceItemsCommand.GetValues();
            getPRIResourceItems.Should().BeEquivalentTo(new[] { "ResourcesResw.resw" });

            // Validate Content items.
            var getContentItemsCommand = new GetValuesCommand(Log, projectFolder, testProject.TargetFrameworks, "Content", GetValuesCommand.ValueType.Item);
            getContentItemsCommand.Execute()
                .Should()
                .Pass();

            var getContentItems = getContentItemsCommand.GetValues();
            getContentItems.Should().BeEquivalentTo(imageFiles);
        }

        [Fact]
        public void It_does_not_include_Windows_App_SDK_items_if_Windows_App_SDK_is_absent()
        {
            var testProject = new TestProject()
            {
                Name = "DontIncludeSourceFilesInNone",
                TargetFrameworks = ToolsetInfo.CurrentTargetFramework,
                IsExe = true,
            };

            // Not setting the "EnableDefaultWindowsAppSdkContentItems" or "EnableDefaultWindowsAppSdkPRIResourceItems" properties!

            var testAsset = _testAssetsManager.CreateTestProject(testProject);
            var projectFolder = Path.Combine(testAsset.TestRoot, testProject.Name);

            File.WriteAllText(Path.Combine(projectFolder, "ResourcesResw.resw"), "<root/>");
            List<string> imageFiles = new() { "TestImage1.png", "TestImage2.bmp", "TestImage3.jpg", "TestImage4.dds", "TestImage5.tif", "TestImage6.tga", "TestImage7.gif" };
            foreach (string fileName in imageFiles)
            {
                File.WriteAllText(Path.Combine(projectFolder, fileName), "");
            }

            // Validate None items.
            var getNoneItemsCommand = new GetValuesCommand(Log, projectFolder, testProject.TargetFrameworks, "None", GetValuesCommand.ValueType.Item);
            getNoneItemsCommand.Execute()
                .Should()
                .Pass();

            var getNoneItems = getNoneItemsCommand.GetValues();
            List<string> expectedFiles = imageFiles;
            expectedFiles.Add("ResourcesResw.resw");
            getNoneItems.Should().BeEquivalentTo(expectedFiles.ToArray());

            // Validate PRIResource items.
            var getPRIResourceItemsCommand = new GetValuesCommand(Log, projectFolder, testProject.TargetFrameworks, "PRIResource", GetValuesCommand.ValueType.Item);
            getPRIResourceItemsCommand.Execute()
                .Should()
                .Pass();

            getPRIResourceItemsCommand.GetValues()
                .Should()
                .BeEmpty();

            // Validate Content items.
            var getContentItemsCommand = new GetValuesCommand(Log, projectFolder, testProject.TargetFrameworks, "Content", GetValuesCommand.ValueType.Item);
            getContentItemsCommand.Execute()
                .Should()
                .Pass();

            getContentItemsCommand.GetValues()
                .Should()
                .BeEmpty();
        }

        void RemoveGeneratedCompileItems(List<string> compileItems)
        {
            //  Remove auto-generated compile items.
            //  TargetFrameworkAttribute comes from %TEMP%\{TargetFrameworkMoniker}.AssemblyAttributes.cs
            //  Other default attributes generated by .NET SDK (for example AssemblyDescriptionAttribute and AssemblyTitleAttribute) come from
            //      { AssemblyName}.AssemblyInfo.cs in the intermediate output path
            var itemsToRemove = compileItems.Where(i =>
                    i.EndsWith("AssemblyAttributes.cs", StringComparison.OrdinalIgnoreCase) ||
                    i.EndsWith("AssemblyInfo.cs", StringComparison.OrdinalIgnoreCase))
                .ToList();

            foreach (var itemToRemove in itemsToRemove)
            {
                compileItems.Remove(itemToRemove);
            }
        }

        private void WriteFile(string path, string contents)
        {
            string folder = Path.GetDirectoryName(path);
            Directory.CreateDirectory(folder);
            File.WriteAllText(path, contents);
        }
    }
}
