File: GivenThatWeWantToBuildADesktopLibrary.cs
Web Access
Project: ..\..\..\test\Microsoft.NET.Build.Tests\Microsoft.NET.Build.Tests.csproj (Microsoft.NET.Build.Tests)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
#nullable disable
 
using System.Security.Cryptography;
using Microsoft.Build.Utilities;
using Microsoft.Extensions.DependencyModel;
 
namespace Microsoft.NET.Build.Tests
{
    public class GivenThatWeWantToBuildADesktopLibrary : SdkTest
    {
        public GivenThatWeWantToBuildADesktopLibrary(ITestOutputHelper log) : base(log)
        {
        }
 
        [WindowsOnlyFact]
        public void It_gets_implicit_designtime_facades_when_package_reference_uses_system_runtime()
        {
            // The repro here is very sensitive to the target framework and packages used. This specific case
            // net46 using only System.Collections.Immutable v1.4.0 will not pull in System.Runtime from a
            // package or from Microsoft.NET.Build.Extensions as a primary reference and so RARs dependency
            // walk needs to find it in order for ImplicitlyExpandDesignTimeFacades to inject it.
 
            var netFrameworkLibrary = new TestProject()
            {
                Name = "NETFrameworkLibrary",
                TargetFrameworks = "net46",
            };
 
            netFrameworkLibrary.PackageReferences.Add(new TestPackageReference("System.Collections.Immutable", "1.4.0"));
 
            netFrameworkLibrary.SourceFiles["NETFramework.cs"] = @"
                using System.Collections.Immutable;
                public class NETFramework
                {
                    public void Method1()
                    {
                        ImmutableList<string>.Empty.Add("""");
                    }
                }";
 
            var testAsset = _testAssetsManager.CreateTestProject(netFrameworkLibrary, "FacadesFromTargetFramework");
            var buildCommand = new BuildCommand(testAsset);
            buildCommand.Execute().Should().Pass();
        }
 
        [WindowsOnlyFact]
        public void It_can_use_HttpClient_and_exchange_the_type_with_a_NETStandard_library()
        {
            var netStandardLibrary = new TestProject()
            {
                Name = "NETStandardLibrary",
                TargetFrameworks = "netstandard1.4",
            };
 
            netStandardLibrary.SourceFiles["NETStandard.cs"] = @"
using System.Net.Http;
public class NETStandard
{
    public static HttpClient GetHttpClient()
    {
        return new HttpClient();
    }
}
";
 
            var netFrameworkLibrary = new TestProject()
            {
                Name = "NETFrameworkLibrary",
                TargetFrameworks = "net462",
            };
 
            netFrameworkLibrary.ReferencedProjects.Add(netStandardLibrary);
 
            netFrameworkLibrary.SourceFiles["NETFramework.cs"] = @"
using System.Net.Http;
public class NETFramework
{
    public void Method1()
    {
        System.Net.Http.HttpClient client = NETStandard.GetHttpClient();
    }
}
";
 
            var testAsset = _testAssetsManager.CreateTestProject(netFrameworkLibrary, "ExchangeHttpClient")
                .WithProjectChanges((projectPath, project) =>
                {
                    if (Path.GetFileName(projectPath).Equals(netFrameworkLibrary.Name + ".csproj", StringComparison.OrdinalIgnoreCase))
                    {
                        var ns = project.Root.Name.Namespace;
 
                        var itemGroup = new XElement(ns + "ItemGroup");
                        project.Root.Add(itemGroup);
 
                        itemGroup.Add(new XElement(ns + "Reference", new XAttribute("Include", "System.Net.Http")));
                    }
                });
 
            var buildCommand = new BuildCommand(testAsset);
 
            buildCommand
                .Execute()
                .Should()
                .Pass();
 
        }
 
        [Theory]
        [InlineData("RazorSimpleMvc22", "netcoreapp2.2", "SimpleMvc22")]
        [InlineData("DesktopReferencingNetStandardLibrary", "net46", "Library")]
        public void PackageReferences_with_private_assets_do_not_appear_in_deps_file(string asset, string targetFramework, string exeName)
        {
            var testAsset = _testAssetsManager
                .CopyTestAsset(asset)
                .WithSource();
 
            var buildCommand = new BuildCommand(testAsset);
            buildCommand.Execute().Should().Pass();
 
            using (var depsJsonFileStream = File.OpenRead(Path.Combine(buildCommand.GetOutputDirectory(targetFramework).FullName, exeName + ".deps.json")))
            {
                var dependencyContext = new DependencyContextJsonReader().Read(depsJsonFileStream);
                if (asset.Equals("DesktopReferencingNetStandardLibrary"))
                {
                    dependencyContext.CompileLibraries.Any(l => l.Name.Equals("Library")).Should().BeTrue();
                    dependencyContext.RuntimeLibraries.Any(l => l.Name.Equals("Library")).Should().BeTrue();
                }
                else
                {
                    dependencyContext.CompileLibraries.Any(l => l.Name.Equals("Nerdbank.GitVersioning")).Should().BeTrue();
                    dependencyContext.RuntimeLibraries.Any(l => l.Name.Equals("Nerdbank.GitVersioning")).Should().BeFalse();
                }
            }
        }
 
        [Theory]
        [InlineData(true)]
        [InlineData(false)]
        public void PackageWithoutAssets_ShouldNotShowUpInDepsJson(bool trimLibrariesWithoutAssets)
        {
            var testProject = new TestProject()
            {
                TargetFrameworks = ToolsetInfo.CurrentTargetFramework
            };
            testProject.PackageReferences.Add(new TestPackageReference("Nerdbank.GitVersioning", "3.6.146"));
 
            if (!trimLibrariesWithoutAssets)
            {
                testProject.AdditionalProperties["TrimDepsJsonLibrariesWithoutAssets"] = "False";
            }
 
            var testAsset = _testAssetsManager.CreateTestProject(testProject);
 
            var buildCommand = new BuildCommand(testAsset);
            buildCommand.Execute().Should().Pass();
 
            using (var depsJsonFileStream = File.OpenRead(Path.Combine(buildCommand.GetOutputDirectory(ToolsetInfo.CurrentTargetFramework).FullName, $"{testProject.Name}.deps.json")))
            {
                var dependencyContext = new DependencyContextJsonReader().Read(depsJsonFileStream);
                if (trimLibrariesWithoutAssets)
                {
                    dependencyContext.RuntimeLibraries.Any(l => l.Name.Equals("Nerdbank.GitVersioning")).Should().BeFalse();
                }
                else
                {
                    dependencyContext.RuntimeLibraries.Any(l => l.Name.Equals("Nerdbank.GitVersioning")).Should().BeTrue();
                }
            }
        }
 
        [Fact]
        public void XUnitCoreIsNotTrimmed()
        {
            //  Regression test for https://github.com/dotnet/sdk/issues/49248
 
            var testProject = new TestProject();
            testProject.PackageReferences.Add(new TestPackageReference("xunit.core", "2.9.3"));
            testProject.PackageReferences.Add(new TestPackageReference("xunit.extensibility.core", "2.9.3"));
            testProject.PackageReferences.Add(new TestPackageReference("xunit.extensibility.execution", "2.9.3"));
 
            var testAsset = _testAssetsManager.CreateTestProject(testProject);
 
            var buildCommand = new BuildCommand(testAsset);
            buildCommand.Execute().Should().Pass();
 
            using (var depsJsonFileStream = File.OpenRead(Path.Combine(buildCommand.GetOutputDirectory(ToolsetInfo.CurrentTargetFramework).FullName, $"{testProject.Name}.deps.json")))
            {
                var dependencyContext = new DependencyContextJsonReader().Read(depsJsonFileStream);
                dependencyContext.RuntimeLibraries.Any(l => l.Name.Equals("xunit.core")).Should().BeTrue();
            }
        }
 
        [Fact]
        public void ProjectNameCanMatchPackageReferenceName()
        {
            var testProject = new TestProject()
            {
                Name = "Newtonsoft.Json",
                TargetFrameworks = ToolsetInfo.CurrentTargetFramework,
            };
 
            testProject.PackageReferences.Add(new TestPackageReference("Newtonsoft.Json", ToolsetInfo.GetNewtonsoftJsonPackageVersion()));
            testProject.AdditionalProperties["PackageId"] = "Newtonsoft.Json*";
 
            var testAsset = _testAssetsManager.CreateTestProject(testProject);
 
            var buildCommand = new BuildCommand(testAsset);
            buildCommand.Execute().Should().Pass();
        }
 
        [WindowsOnlyFact]
        public void It_can_reference_a_netstandard2_library_and_exchange_types()
        {
            var netStandardLibrary = new TestProject()
            {
                Name = "NETStandardLibrary",
                TargetFrameworks = "netstandard2.0",
            };
 
            netStandardLibrary.SourceFiles["NETStandard.cs"] = @"
public class NETStandard
{
    public static string GetString()
    {
        return ""Hello from netstandard2.0 library."";
    }
}
";
            var netFrameworkLibrary = new TestProject()
            {
                Name = "NETFrameworkLibrary",
                TargetFrameworks = "net462",
            };
            netFrameworkLibrary.ReferencedProjects.Add(netStandardLibrary);
 
            netFrameworkLibrary.SourceFiles["NETFramework.cs"] = @"
public class NETFramework
{
    public void Method1()
    {
        string result = NETStandard.GetString();
    }
}
";
            var testAsset = _testAssetsManager.CreateTestProject(netFrameworkLibrary, "ExchangeNETStandard2");
 
            var buildCommand = new BuildCommand(testAsset);
 
            buildCommand
                .Execute()
                .Should()
                .Pass();
        }
 
        [WindowsOnlyFact]
        public void It_can_use_ValueTuple_and_exchange_the_type_with_a_NETStandard_library()
        {
            var referenceAssemblies = ToolLocationHelper.GetPathToDotNetFrameworkReferenceAssemblies(TargetDotNetFrameworkVersion.Version47);
            if (!Directory.Exists(referenceAssemblies))
            {
                return;
            }
 
            var netStandardLibrary = new TestProject()
            {
                Name = "NETStandardLibrary",
                TargetFrameworks = "netstandard2.0",
            };
 
            netStandardLibrary.SourceFiles["NETStandard.cs"] = @"
public class NETStandard
{
    public static (int x, int y) GetCoordinates()
    {
        return (1, 10);
    }
}
";
            // ValueTuple was moved into mscorlib in net47, make sure we include net47-specific build of netstandard libs
            // that typeforward to mscorlib.
            var netFrameworkLibrary = new TestProject()
            {
                Name = "NETFrameworkLibrary",
                TargetFrameworks = "net47",
            };
            netFrameworkLibrary.ReferencedProjects.Add(netStandardLibrary);
 
            netFrameworkLibrary.SourceFiles["NETFramework.cs"] = @"
using System;
 
public class NETFramework
{
    public void Method1()
    {
        var coords = NETStandard.GetCoordinates();
        Console.WriteLine($""({coords.x}, {coords.y})"");
        ValueTuple<int,int> vt = coords;
    }
}
";
            var testAsset = _testAssetsManager.CreateTestProject(netFrameworkLibrary, "ExchangeValueTuple");
 
            var buildCommand = new BuildCommand(testAsset);
 
            buildCommand
                .Execute()
                .Should()
                .Pass();
        }
 
        [WindowsOnlyFact]
        public void It_can_preserve_compilation_context_and_reference_netstandard_library()
        {
            var testAsset = _testAssetsManager
                .CopyTestAsset("DesktopReferencingNetStandardLibrary")
                .WithSource();
 
            var buildCommand = new BuildCommand(testAsset);
            buildCommand
                .Execute()
                .Should().Pass();
 
            using (var depsJsonFileStream = File.OpenRead(Path.Combine(buildCommand.GetOutputDirectory("net46").FullName, "Library.deps.json")))
            {
                var dependencyContext = new DependencyContextJsonReader().Read(depsJsonFileStream);
                dependencyContext.CompileLibraries.Should().NotBeEmpty();
            }
        }
 
        [WindowsOnlyFact]
        public void It_resolves_assembly_conflicts_with_a_NETFramework_library()
        {
            TestProject project = new()
            {
                Name = "NETFrameworkLibrary",
                TargetFrameworks = "net462",
            };
 
            project.SourceFiles[project.Name + ".cs"] = $@"
using System;
public static class {project.Name}
{{
    {ConflictResolutionAssets.ConflictResolutionTestMethod}
}}";
 
            var testAsset = _testAssetsManager.CreateTestProject(project)
                .WithProjectChanges(p =>
                {
                    var ns = p.Root.Name.Namespace;
 
                    var itemGroup = new XElement(ns + "ItemGroup");
                    p.Root.Add(itemGroup);
 
                    itemGroup.Add(new XElement(ns + "PackageReference",
                        new XAttribute("Include", "NETStandard.Library"),
                        new XAttribute("Version", "2.0.3")));
 
                    foreach (var dependency in ConflictResolutionAssets.ConflictResolutionDependencies)
                    {
                        itemGroup.Add(new XElement(ns + "PackageReference",
                            new XAttribute("Include", dependency.Item1),
                            new XAttribute("Version", dependency.Item2)));
                    }
 
                });
 
            var buildCommand = new BuildCommand(testAsset);
 
            buildCommand
                .Execute()
                .Should()
                .Pass()
                .And
                .NotHaveStdOutContaining("warning")
                .And
                .NotHaveStdOutContaining("MSB3243");
        }
 
        [WindowsOnlyTheory]
        [InlineData(false)]
        [InlineData(true)]
        public void It_uses_hintpath_when_replacing_simple_name_references(bool useFacades)
        {
            TestProject project = new()
            {
                Name = "NETFrameworkLibrary",
                TargetFrameworks = "net462",
            };
 
            if (useFacades)
            {
                var netStandard2Project = new TestProject()
                {
                    Name = "NETStandard20Project",
                    TargetFrameworks = "netstandard2.0",
                };
 
                project.ReferencedProjects.Add(netStandard2Project);
            }
 
 
            var testAsset = _testAssetsManager.CreateTestProject(project, "SimpleNamesWithHintPaths", identifier: useFacades ? "_useFacades" : "")
                .WithProjectChanges((path, p) =>
                {
                    if (Path.GetFileNameWithoutExtension(path) == project.Name)
                    {
                        var ns = p.Root.Name.Namespace;
 
                        var itemGroup = new XElement(ns + "ItemGroup");
                        p.Root.Add(itemGroup);
 
                        if (!useFacades)
                        {
                            itemGroup.Add(new XElement(ns + "PackageReference",
                                new XAttribute("Include", "System.Net.Http"),
                                new XAttribute("Version", "4.3.2")));
                        }
 
                        itemGroup.Add(new XElement(ns + "Reference",
                            new XAttribute("Include", "System.Net.Http")));
                    }
                });
 
            string projectFolder = Path.Combine(testAsset.Path, project.Name);
 
            var getValuesCommand = new GetValuesCommand(Log, projectFolder, project.TargetFrameworks, "Reference", GetValuesCommand.ValueType.Item);
            getValuesCommand.MetadataNames.Add("HintPath");
 
            getValuesCommand
                .Execute()
                .Should()
                .Pass();
 
            string correctHttpReference;
            if (useFacades)
            {
                string microsoftNETBuildExtensionsPath = TestContext.Current.ToolsetUnderTest.GetMicrosoftNETBuildExtensionsPath();
                correctHttpReference = Path.Combine(microsoftNETBuildExtensionsPath, @"net461\lib\System.Net.Http.dll");
            }
            else
            {
                correctHttpReference = Path.Combine(TestContext.Current.NuGetCachePath, "system.net.http", "4.3.2", "ref", "net46", "System.Net.Http.dll");
            }
 
            var valuesWithMetadata = getValuesCommand.GetValuesWithMetadata();
 
            //  There shouldn't be a Reference item where the ItemSpec is the path to the System.Net.Http.dll from a NuGet package
            valuesWithMetadata.Should().NotContain(v => v.value == correctHttpReference);
 
            //  There should be a Reference item where the ItemSpec is the simple name System.Net.Http
            valuesWithMetadata.Should().ContainSingle(v => v.value == "System.Net.Http");
 
            //  The Reference item with the simple name should have a HintPath to the DLL in the NuGet package
            valuesWithMetadata.Single(v => v.value == "System.Net.Http")
                .metadata["HintPath"]
                .Should().Be(correctHttpReference);
        }
 
        [Fact]
        public void It_tolerates_newline_in_hint_path()
        {
            string hintPath = BuildReferencedBuildAndReturnOutputDllPath();
 
            TestProject project = new()
            {
                Name = "NETFrameworkLibrary",
                TargetFrameworks = "net462",
            };
 
            TestAsset testAsset = _testAssetsManager.CreateTestProject(project, "SimpleNamesWithHintPathsWithNewLines")
                .WithProjectChanges((path, p) =>
                {
                    XNamespace ns = p.Root.Name.Namespace;
 
                    var itemGroup = new XElement(ns + "ItemGroup");
                    p.Root.Add(itemGroup);
 
                    itemGroup.Add(
                        new XElement(ns + "Reference",
                            new XAttribute("Include", "System.Net.Http"),
                            new XElement("HintPath", $"   {Environment.NewLine}{hintPath}   {Environment.NewLine}")));
                });
 
            var buildCommand = new BuildCommand(testAsset);
            var msbuildBuildCommand = new MSBuildCommand(Log, "Build", buildCommand.FullPathProjectFile);
            msbuildBuildCommand.Execute().Should().Pass()
                .And.NotHaveStdOutContaining("System.ArgumentException");
        }
 
        private string BuildReferencedBuildAndReturnOutputDllPath()
        {
            TestProject referencedProject = new()
            {
                Name = "NETFrameworkLibrary",
                TargetFrameworks = "net462",
            };
 
            TestAsset referencedTestAsset = _testAssetsManager
                .CreateTestProject(referencedProject, "SimpleNamesWithHintPathsWithNewLinesReferenced");
 
            var referencedbuildCommand =
                new BuildCommand(referencedTestAsset);
 
            referencedbuildCommand.Execute();
 
            DirectoryInfo outputDirectory = referencedbuildCommand.GetOutputDirectory(
                referencedProject.TargetFrameworks);
            return new FileInfo(Path.Combine(outputDirectory.FullName, referencedProject.Name + ".dll")).FullName;
        }
 
        //  Regression test for https://github.com/dotnet/sdk/issues/1730
        [WindowsOnlyFact]
        public void A_target_can_depend_on_RunResolvePublishAssemblies()
        {
            TestProject testProject = new()
            {
                Name = "DependsOnPublish",
                TargetFrameworks = "net462",
                IsExe = false
            };
 
            var testInstance = _testAssetsManager.CreateTestProject(testProject, testProject.Name)
                .WithProjectChanges(p =>
                {
                    var pns = p.Root.Name.Namespace;
 
                    p.Root.Add(new XElement(pns + "Target",
                        new XAttribute("Name", "Repro"),
                        new XAttribute("DependsOnTargets", "RunResolvePublishAssemblies"),
                        new XAttribute("BeforeTargets", "BeforeBuild")));
                });
 
            var buildCommand = new BuildCommand(testInstance);
 
            buildCommand.Execute()
                .Should()
                .Pass();
        }
    }
}