File: GivenThatWeWantToVerifyNuGetReferenceCompat.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.Runtime.CompilerServices;
 
namespace Microsoft.NET.Build.Tests
{
    public class GivenThatWeWantToVerifyNuGetReferenceCompat : SdkTest, IClassFixture<DeleteNuGetArtifactsFixture>
    {
        public GivenThatWeWantToVerifyNuGetReferenceCompat(ITestOutputHelper log) : base(log)
        {
        }
 
        [Theory]
        [InlineData("net45", "Full", "netstandard1.0 netstandard1.1 net45", true, true)]
        [InlineData("net462", "Full", "netstandard1.0 netstandard1.1 netstandard1.2 netstandard1.3 netstandard1.4 netstandard1.5 netstandard1.6 netstandard2.0 net45 net451 net46 net461 net462", true, true)]
        [InlineData("netstandard1.6", "Full", "netstandard1.0 netstandard1.1 netstandard1.2 netstandard1.3 netstandard1.4 netstandard1.5 netstandard1.6", true, true)]
        [InlineData("netstandard2.0", "Full", "netstandard1.0 netstandard1.1 netstandard1.2 netstandard1.3 netstandard1.4 netstandard1.5 netstandard1.6 netstandard2.0", true, true)]
        [InlineData("netcoreapp2.0", "Full", "netstandard1.0 netstandard1.1 netstandard1.2 netstandard1.3 netstandard1.4 netstandard1.5 netstandard1.6 netstandard2.0 netcoreapp1.0 netcoreapp1.1 netcoreapp2.0", true, true)]
 
        [InlineData("netstandard2.0", "OptIn", "net45 net451 net46 net461", true, true)]
        [InlineData("netcoreapp2.0", "OptIn", "net45 net451 net46 net461", true, true)]
 
        public void Nuget_reference_compat(string referencerTarget, string testDescription, string rawDependencyTargets,
                bool restoreSucceeds, bool buildSucceeds)
        {
            string referencerDirectoryNamePostfix = "_" + referencerTarget + "_" + testDescription;
 
            TestProject referencerProject = GetTestProject(ConstantStringValues.ReferencerDirectoryName, referencerTarget, true);
 
            //  Skip running test if not running on Windows
            //        https://github.com/dotnet/sdk/issues/335
            if (!(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) || referencerProject.BuildsOnNonWindows))
            {
                return;
            }
 
            var dependencyPackageReferences = new List<TestPackageReference>();
 
            // Process all dependencies in parallel
            Parallel.ForEach(
                rawDependencyTargets.Split(',', ';', ' ').Where(s => !string.IsNullOrWhiteSpace(s)),
                new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount },
                dependencyTarget =>
                {
                    // Create the dependency project and package
                    TestProject dependencyProject = GetTestProject(
                        ConstantStringValues.DependencyDirectoryNamePrefix + dependencyTarget.Replace('.', '_'),
                        dependencyTarget,
                        true);
 
                    TestPackageReference dependencyPackageReference = new(
                        dependencyProject.Name,
                        "1.0.0",
                        ConstantStringValues.ConstructNuGetPackageReferencePath(dependencyProject, identifier: referencerTarget + testDescription + rawDependencyTargets));
 
                    // Create package if it doesn't exist
                    if (!dependencyPackageReference.NuGetPackageExists() &&
                        (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) || dependencyProject.BuildsOnNonWindows))
                    {
                        if (!dependencyPackageReference.NuGetPackageExists())
                        {
                            var dependencyTestAsset = _testAssetsManager.CreateTestProject(
                                dependencyProject,
                                identifier: referencerTarget + testDescription + rawDependencyTargets);
 
                            dependencyTestAsset.GetRestoreCommand(Log, relativePath: dependencyProject.Name)
                                .Execute().Should().Pass();
 
                            var dependencyProjectDirectory = Path.Combine(
                                dependencyTestAsset.TestRoot,
                                dependencyProject.Name);
 
                            new PackCommand(Log, dependencyProjectDirectory)
                                .Execute().Should().Pass();
                        }
 
                    }
                });
 
            // Add all references to the referencer project
            foreach (var dependencyPackageReference in dependencyPackageReferences)
            {
                referencerProject.PackageReferences.Add(dependencyPackageReference);
            }
 
            //  Skip running tests if no NuGet packages are referenced
            //        https://github.com/dotnet/sdk/issues/335
            if (referencerProject.PackageReferences == null)
            {
                return;
            }
 
            //  Set the referencer project as an Exe unless it targets .NET Standard
            if (!referencerProject.TargetFrameworkIdentifiers.Contains(ConstantStringValues.NetstandardTargetFrameworkIdentifier))
            {
                referencerProject.IsExe = true;
            }
 
            //  Create the referencing app and run the compat test
            var referencerTestAsset = _testAssetsManager.CreateTestProject(referencerProject, ConstantStringValues.TestDirectoriesNamePrefix, referencerDirectoryNamePostfix);
            var referencerRestoreCommand = referencerTestAsset.GetRestoreCommand(Log, relativePath: referencerProject.Name);
 
            List<string> referencerRestoreSources = new();
 
            //  Modify the restore command to refer to the created NuGet packages
            foreach (TestPackageReference packageReference in referencerProject.PackageReferences)
            {
                var source = Path.Combine(packageReference.NupkgPath, packageReference.ID, "bin", "Debug");
                referencerRestoreSources.Add(source);
            }
 
            NuGetConfigWriter.Write(referencerTestAsset.TestRoot, referencerRestoreSources);
 
            if (restoreSucceeds)
            {
                referencerRestoreCommand.Execute().Should().Pass();
            }
            else
            {
                referencerRestoreCommand.Execute().Should().Fail();
            }
 
            var referencerBuildCommand = new BuildCommand(referencerTestAsset);
            var referencerBuildResult = referencerBuildCommand.Execute();
 
            if (buildSucceeds)
            {
                referencerBuildResult.Should().Pass();
            }
            else
            {
                referencerBuildResult.Should().Fail().And.HaveStdOutContaining("It cannot be referenced by a project that targets");
            }
        }
 
        [WindowsOnlyTheory]
        [InlineData("netstandard2.0")]
        [InlineData("netcoreapp2.0")]
        public void Netfx_is_implicit_for_Netstandard_and_Netcore_20(string targetFramework)
        {
            var testProjectName = targetFramework.Replace(".", "_") + "implicit_atf";
 
            var (testProjectTestAsset, testPackageReference) = CreateTestAsset(testProjectName, targetFramework, "net461", identifer: targetFramework);
 
            var restoreCommand = testProjectTestAsset.GetRestoreCommand(Log, relativePath: testProjectName);
 
            var source = Path.Combine(testPackageReference.NupkgPath, testPackageReference.ID, "bin", "Debug");
            NuGetConfigWriter.Write(testProjectTestAsset.TestRoot, source);
 
            restoreCommand.Execute().Should().Pass();
 
            var buildCommand = new BuildCommand(testProjectTestAsset);
            buildCommand.Execute().Should().Pass();
        }
 
        [WindowsOnlyTheory]
        [InlineData("netstandard1.6")]
        [InlineData("netcoreapp1.1")]
        public void Netfx_is_not_implicit_for_Netstandard_and_Netcore_less_than_20(string targetFramework)
        {
            var testProjectName = targetFramework.Replace(".", "_") + "non_implicit_atf";
 
            var (testProjectTestAsset, testPackageReference) = CreateTestAsset(testProjectName, targetFramework, "net461", identifer: targetFramework);
 
            var restoreCommand = testProjectTestAsset.GetRestoreCommand(Log, relativePath: testProjectName);
            NuGetConfigWriter.Write(testProjectTestAsset.TestRoot, Path.GetDirectoryName(testPackageReference.NupkgPath));
            restoreCommand.Execute().Should().Fail();
        }
 
        [WindowsOnlyFact]
        public void It_is_possible_to_disable_netfx_implicit_asset_target_fallback()
        {
            const string testProjectName = "netstandard20_disabled_atf";
 
            var (testProjectTestAsset, testPackageReference) = CreateTestAsset(
                testProjectName,
                "netstandard2.0",
                "net461",
                new Dictionary<string, string> { { "DisableImplicitAssetTargetFallback", "true" } });
 
            var restoreCommand = testProjectTestAsset.GetRestoreCommand(Log, relativePath: testProjectName);
            NuGetConfigWriter.Write(testProjectTestAsset.TestRoot, Path.GetDirectoryName(testPackageReference.NupkgPath));
            restoreCommand.Execute().Should().Fail();
        }
 
        [WindowsOnlyFact]
        public void It_chooses_lowest_netfx_in_default_atf()
        {
            var testProjectName = $"{ToolsetInfo.CurrentTargetFramework.Replace(".", "")}_multiple_atf";
 
            var (testProjectTestAsset, testPackageReference) = CreateTestAsset(
               testProjectName,
               ToolsetInfo.CurrentTargetFramework,
               "net462;net472",
               new Dictionary<string, string> { ["CopyLocalLockFileAssemblies"] = "true" });
 
 
            var source = Path.Combine(testPackageReference.NupkgPath, testPackageReference.ID, "bin", "Debug");
            NuGetConfigWriter.Write(testProjectTestAsset.TestRoot, source);
 
            var restoreCommand = testProjectTestAsset.GetRestoreCommand(Log, relativePath: testProjectName);
            restoreCommand.Execute().Should().Pass();
 
            var buildCommand = new BuildCommand(testProjectTestAsset);
            buildCommand.Execute().Should().Pass();
 
            var referencedDll = buildCommand.GetOutputDirectory().File("net462_net472_pkg.dll").FullName;
            var referencedTargetFramework = AssemblyInfo.Get(referencedDll).Where(i => i.Key == "TargetFrameworkAttribute").Single().Value;
            referencedTargetFramework.Should().Be(".NETFramework,Version=v4.6.2");
        }
 
        private (TestAsset, TestPackageReference) CreateTestAsset(
            string testProjectName,
            string callerTargetFramework,
            string calleeTargetFrameworks,
            Dictionary<string, string> additionalProperties = null,
            [CallerMemberName] string testName = null,
            string identifer = null)
        {
            var testPackageReference = CreateTestPackage(calleeTargetFrameworks, testName, identifer);
 
            var testProject =
                new TestProject
                {
                    Name = testProjectName,
                    TargetFrameworks = callerTargetFramework,
                };
 
            if (additionalProperties != null)
            {
                foreach (var additionalProperty in additionalProperties)
                {
                    testProject.AdditionalProperties.Add(additionalProperty.Key, additionalProperty.Value);
                }
            }
 
            testProject.PackageReferences.Add(testPackageReference);
 
            var testProjectTestAsset = _testAssetsManager.CreateTestProject(
                testProject,
                string.Empty,
                $"{testProjectName}_{calleeTargetFrameworks}");
 
            return (testProjectTestAsset, testPackageReference);
        }
 
        private TestPackageReference CreateTestPackage(string targetFrameworks, string identifier, [CallerMemberName] string callingMethod = "")
        {
            var project =
                new TestProject
                {
                    Name = $"{targetFrameworks.Replace(';', '_')}_pkg",
                    TargetFrameworks = targetFrameworks,
                };
 
            var packageReference =
                new TestPackageReference(
                    project.Name,
                    "1.0.0",
                    ConstantStringValues.ConstructNuGetPackageReferencePath(project, identifier, callingMethod));
 
            if (!packageReference.NuGetPackageExists())
            {
                var testAsset =
                    _testAssetsManager.CreateTestProject(
                        project,
                        callingMethod,
                        identifier);
                var packageRestoreCommand =
                    testAsset.GetRestoreCommand(Log, relativePath: project.Name).Execute().Should().Pass();
                var dependencyProjectDirectory = Path.Combine(testAsset.TestRoot, project.Name);
                var packagePackCommand =
                    new PackCommand(Log, dependencyProjectDirectory).Execute().Should().Pass();
            }
 
            return packageReference;
        }
 
        TestProject GetTestProject(string name, string target, bool isSdkProject)
        {
            TestProject ret = new()
            {
                Name = name,
                IsSdkProject = isSdkProject
            };
 
            if (isSdkProject)
            {
                ret.TargetFrameworks = target;
            }
            else
            {
                ret.TargetFrameworkVersion = target;
            }
 
            return ret;
        }
    }
}