From 6a676280564ce8e04f10c29fe5ebc346aa462ca1 Mon Sep 17 00:00:00 2001 From: Elinor Fung Date: Thu, 10 Nov 2022 09:17:05 -0800 Subject: [PATCH] Switch `LightupAppActivation` tests to component tests targeting additional deps (#78139) The `LightupAppActivation` tests are testing handling of additional deps. The actual app and lib are not really interesting, so we can switch them to the dependency resolution tests that use a mock coreclr and don't actually run an app. On my Windows machine, this goes from ~20s to run the deleted tests to ~700ms to run the new tests. --- .../LightupClient/LightupClient.csproj | 10 - .../TestProjects/LightupClient/Program.cs | 39 -- .../TestProjects/LightupLib/LightupLib.csproj | 13 - .../Assets/TestProjects/LightupLib/Program.cs | 21 - .../DependencyResolution/AdditionalDeps.cs | 170 ++++++ ...ndencyResolutionCommandResultExtensions.cs | 4 + .../PerAssemblyVersionResolution.cs | 40 ++ .../LightupAppActivation.cs | 569 ------------------ src/installer/tests/TestUtils/Constants.cs | 5 + .../tests/TestUtils/SharedFramework.cs | 198 +----- 10 files changed, 223 insertions(+), 846 deletions(-) delete mode 100644 src/installer/tests/Assets/TestProjects/LightupClient/LightupClient.csproj delete mode 100644 src/installer/tests/Assets/TestProjects/LightupClient/Program.cs delete mode 100644 src/installer/tests/Assets/TestProjects/LightupLib/LightupLib.csproj delete mode 100644 src/installer/tests/Assets/TestProjects/LightupLib/Program.cs create mode 100644 src/installer/tests/HostActivation.Tests/DependencyResolution/AdditionalDeps.cs delete mode 100644 src/installer/tests/HostActivation.Tests/LightupAppActivation.cs diff --git a/src/installer/tests/Assets/TestProjects/LightupClient/LightupClient.csproj b/src/installer/tests/Assets/TestProjects/LightupClient/LightupClient.csproj deleted file mode 100644 index 06ac4b1d7256b..0000000000000 --- a/src/installer/tests/Assets/TestProjects/LightupClient/LightupClient.csproj +++ /dev/null @@ -1,10 +0,0 @@ - - - - $(NetCoreAppCurrent) - Exe - $(MNAVersion) - true - - - diff --git a/src/installer/tests/Assets/TestProjects/LightupClient/Program.cs b/src/installer/tests/Assets/TestProjects/LightupClient/Program.cs deleted file mode 100644 index 131198bd890d2..0000000000000 --- a/src/installer/tests/Assets/TestProjects/LightupClient/Program.cs +++ /dev/null @@ -1,39 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.IO; -using System.Reflection; - -namespace LightupClient -{ - public static class Program - { - public static int Main(string[] args) - { - Assembly asmGreet = null; - int iRetVal = 0; - - try - { - asmGreet = Assembly.Load(new AssemblyName("LightupLib")); - - // Get reference to the method that we wish to invoke - Type type = asmGreet.GetType("LightupLib.Greet"); - var method = System.Reflection.TypeExtensions.GetMethod(type, "Hello"); - - // Invoke it - string greeting = (string)method.Invoke(null, new object[] {"LightupClient"}); - Console.WriteLine("{0}", greeting); - } - catch(FileNotFoundException ex) - { - Console.WriteLine("Exception: Failed to load the lightup assembly!"); - Console.WriteLine(ex.ToString()); - iRetVal = -1; - } - - return iRetVal; - } - } -} diff --git a/src/installer/tests/Assets/TestProjects/LightupLib/LightupLib.csproj b/src/installer/tests/Assets/TestProjects/LightupLib/LightupLib.csproj deleted file mode 100644 index 976a8423304a0..0000000000000 --- a/src/installer/tests/Assets/TestProjects/LightupLib/LightupLib.csproj +++ /dev/null @@ -1,13 +0,0 @@ - - - - $(NetCoreAppCurrent) - Library - $(MNAVersion) - - - - - - - diff --git a/src/installer/tests/Assets/TestProjects/LightupLib/Program.cs b/src/installer/tests/Assets/TestProjects/LightupLib/Program.cs deleted file mode 100644 index 2b70d4fea43b8..0000000000000 --- a/src/installer/tests/Assets/TestProjects/LightupLib/Program.cs +++ /dev/null @@ -1,21 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Reflection; - -namespace LightupLib -{ - public class Greet - { - public static string Hello(string name) - { - // Load a dependency of LightupLib - var t = typeof(Newtonsoft.Json.JsonReader); - if (t != null) - return "Hello "+name; - else - return "Failed to load LibDependency"; - } - } -} diff --git a/src/installer/tests/HostActivation.Tests/DependencyResolution/AdditionalDeps.cs b/src/installer/tests/HostActivation.Tests/DependencyResolution/AdditionalDeps.cs new file mode 100644 index 0000000000000..f97c7c8c59702 --- /dev/null +++ b/src/installer/tests/HostActivation.Tests/DependencyResolution/AdditionalDeps.cs @@ -0,0 +1,170 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.IO; +using Microsoft.DotNet.Cli.Build; +using Microsoft.DotNet.Cli.Build.Framework; +using Xunit; + +namespace Microsoft.DotNet.CoreSetup.Test.HostActivation.DependencyResolution +{ + public class AdditionalDeps : DependencyResolutionBase, IClassFixture + { + private SharedTestState SharedState { get; } + + // Shared state has a NetCoreApp with the two versions below + public const string NetCoreAppVersion = "4.1.1"; + public const string NetCoreAppVersionPreview = "4.1.2-preview.2"; + + public AdditionalDeps(SharedTestState sharedState) + { + SharedState = sharedState; + } + + // Additional deps can point to a directory. The host looks for deps.json files in: + // /shared// + // It uses the version closest to the framework version with a matching major/minor + // and equal or lesser patch, release or pre-release. + [Theory] + // exact match + [InlineData("4.1.1", new string[] { "4.1.0", "4.1.1" }, "4.1.1")] + [InlineData("4.1.2-preview.2", new string[] { "4.1.1", "4.1.2-preview.2" }, "4.1.2-preview.2")] + // lower patch version + [InlineData("4.1.1", new string[] { "4.1.0", "4.1.2-preview.1" }, "4.1.0")] + [InlineData("4.1.2-preview.2", new string[] { "4.1.1", "4.1.2" }, "4.1.1")] + // lower prerelease + [InlineData("4.1.1", new string[] { "4.1.0", "4.1.1-preview.1" }, "4.1.1-preview.1")] + [InlineData("4.1.2-preview.2", new string[] { "4.1.1", "4.1.2-preview.1" }, "4.1.2-preview.1")] + // no match + [InlineData("4.1.1", new string[] { "4.0.0", "4.1.2", "4.2.0" }, null)] + [InlineData("4.1.2-preview.2", new string[] { "4.0.0", "4.1.2", "4.2.0" }, null)] + public void DepsDirectory(string fxVersion, string[] versions, string usedVersion) + { + string additionalDepsDirectory = SharedFramework.CalculateUniqueTestDirectory(Path.Combine(SharedState.Location, "additionalDeps")); + using (TestArtifact artifact = new TestArtifact(additionalDepsDirectory)) + { + string depsJsonName = Path.GetFileName(SharedState.AdditionalDepsComponent.DepsJson); + foreach (string version in versions) + { + string path = Path.Combine(additionalDepsDirectory, "shared", MicrosoftNETCoreApp, version); + Directory.CreateDirectory(path); + File.Copy( + SharedState.AdditionalDepsComponent.DepsJson, + Path.Combine(path, depsJsonName), + true); + } + + TestApp app = SharedState.FrameworkReferenceApp; + if (fxVersion != NetCoreAppVersion) + { + // Make a copy of the app and update its framework version + app = SharedState.FrameworkReferenceApp.Copy(); + RuntimeConfig.FromFile(app.RuntimeConfigJson) + .RemoveFramework(MicrosoftNETCoreApp) + .WithFramework(MicrosoftNETCoreApp, fxVersion) + .Save(); + } + + CommandResult result = SharedState.DotNetWithNetCoreApp.Exec(Constants.AdditionalDeps.CommandLineArgument, additionalDepsDirectory, app.AppDll) + .EnableTracingAndCaptureOutputs() + .Execute(); + + result.Should().Pass(); + if (string.IsNullOrEmpty(usedVersion)) + { + result.Should().HaveStdErrContaining($"No additional deps directory less than or equal to [{fxVersion}] found with same major and minor version."); + } + else + { + result.Should().HaveUsedAdditionalDeps(Path.Combine(additionalDepsDirectory, "shared", MicrosoftNETCoreApp, usedVersion, depsJsonName)); + } + } + } + + [Theory] + [InlineData(true)] + [InlineData(false)] + public void DepsFile(bool dependencyExists) + { + string additionalLibName = SharedState.AdditionalDepsComponent.AssemblyName; + string additionalDepsFile = SharedState.AdditionalDepsComponent.DepsJson; + + TestApp app = SharedState.FrameworkReferenceApp; + if (!dependencyExists) + { + // Make a copy of the app and delete the app-local dependency + app = SharedState.FrameworkReferenceApp.Copy(); + File.Delete(Path.Combine(app.Location, $"{additionalLibName}.dll")); + } + + CommandResult result = SharedState.DotNetWithNetCoreApp.Exec(Constants.AdditionalDeps.CommandLineArgument, additionalDepsFile, app.AppDll) + .EnableTracingAndCaptureOutputs() + .Execute(expectedToFail: !dependencyExists); + + result.Should().HaveUsedAdditionalDeps(additionalDepsFile); + if (dependencyExists) + { + result.Should().Pass() + .And.HaveResolvedAssembly(Path.Combine(app.Location, $"{additionalLibName}.dll")); + } + else + { + result.Should().Fail() + .And.HaveStdErrContaining( + $"Error:{Environment.NewLine}" + + $" An assembly specified in the application dependencies manifest ({additionalLibName}.deps.json) was not found:" + Environment.NewLine + + $" package: \'{additionalLibName}\', version: \'1.0.0\'" + Environment.NewLine + + $" path: \'{additionalLibName}.dll\'"); + } + } + + [Fact] + public void InvalidJson() + { + string invalidDepsFile = Path.Combine(SharedState.Location, "invalid.deps.json"); + try + { + File.WriteAllText(invalidDepsFile, "{"); + + SharedState.DotNetWithNetCoreApp.Exec(Constants.AdditionalDeps.CommandLineArgument, invalidDepsFile, SharedState.FrameworkReferenceApp.AppDll) + .EnableTracingAndCaptureOutputs() + .Execute(expectedToFail: true) + .Should().Fail() + .And.HaveUsedAdditionalDeps(invalidDepsFile) + .And.HaveStdErrContaining($"Error initializing the dependency resolver: An error occurred while parsing: {invalidDepsFile}"); + } + finally + { + FileUtils.DeleteFileIfPossible(invalidDepsFile); + } + } + + public class SharedTestState : DependencyResolutionBase.SharedTestStateBase + { + public DotNetCli DotNetWithNetCoreApp { get; } + + public TestApp FrameworkReferenceApp { get; } + + public TestApp AdditionalDepsComponent { get; } + + public SharedTestState() + { + DotNetWithNetCoreApp = DotNet("WithNetCoreApp") + .AddMicrosoftNETCoreAppFrameworkMockCoreClr(NetCoreAppVersion) + .AddMicrosoftNETCoreAppFrameworkMockCoreClr(NetCoreAppVersionPreview) + .Build(); + + AdditionalDepsComponent = CreateComponentWithNoDependencies(); + + TestApp app = CreateFrameworkReferenceApp(MicrosoftNETCoreApp, NetCoreAppVersion); + FrameworkReferenceApp = NetCoreAppBuilder.PortableForNETCoreApp(app) + .WithProject(p => p.WithAssemblyGroup(null, g => g.WithMainAssembly())) + .Build(app); + + // Copy dependency next to app + File.Copy(AdditionalDepsComponent.AppDll, Path.Combine(FrameworkReferenceApp.Location, $"{AdditionalDepsComponent.AssemblyName}.dll")); + } + } + } +} diff --git a/src/installer/tests/HostActivation.Tests/DependencyResolution/DependencyResolutionCommandResultExtensions.cs b/src/installer/tests/HostActivation.Tests/DependencyResolution/DependencyResolutionCommandResultExtensions.cs index a4e5d55fd028f..14b44a1628f66 100644 --- a/src/installer/tests/HostActivation.Tests/DependencyResolution/DependencyResolutionCommandResultExtensions.cs +++ b/src/installer/tests/HostActivation.Tests/DependencyResolution/DependencyResolutionCommandResultExtensions.cs @@ -142,6 +142,10 @@ public static AndConstraint NotHaveResolvedComponentDep return assertion.NotHaveResolvedComponentDependencyContaining(native_search_paths, RelativePathsToAbsoluteAppPaths(path, app)); } + public static AndConstraint HaveUsedAdditionalDeps(this CommandResultAssertions assertion, string depsFilePath) + { + return assertion.HaveStdErrContaining($"Using specified additional deps.json: '{depsFilePath}'"); + } private static string GetAppMockPropertyValue(CommandResultAssertions assertion, string propertyName) => GetMockPropertyValue(assertion, $"mock property[{propertyName}] = "); diff --git a/src/installer/tests/HostActivation.Tests/DependencyResolution/PerAssemblyVersionResolution.cs b/src/installer/tests/HostActivation.Tests/DependencyResolution/PerAssemblyVersionResolution.cs index 5140a572c7a64..074ff81f6ea5c 100644 --- a/src/installer/tests/HostActivation.Tests/DependencyResolution/PerAssemblyVersionResolution.cs +++ b/src/installer/tests/HostActivation.Tests/DependencyResolution/PerAssemblyVersionResolution.cs @@ -119,6 +119,46 @@ protected override void RunTest(string testAssemblyName, string appAsmVersion, s } } + public class AdditionalDepsPerAssemblyVersionResolution : + PerAssemblyVersionResolutionBase, + IClassFixture + { + public AdditionalDepsPerAssemblyVersionResolution(SharedTestState sharedState) + : base(sharedState) + { + } + + protected override void RunTest(string testAssemblyName, string appAsmVersion, string appFileVersion, bool appWins) + { + using (TestApp additionalDependency = TestApp.CreateEmpty("additionalDeps")) + { + // Additional deps are treated as part of app dependencies. + // The result for whether the app wins should be the same whether the dependency is + // specified via additional deps or by the app itself. + NetCoreAppBuilder builder = NetCoreAppBuilder.PortableForNETCoreApp(additionalDependency) + .WithPackage(TestVersionsPackage, "1.0.0", lib => lib + .WithAssemblyGroup(null, g => g + .WithAsset(testAssemblyName + ".dll", rf => rf + .WithVersion(appAsmVersion, appFileVersion)))); + builder.Build(additionalDependency); + + TestApp app = SharedState.FrameworkReferenceApp.Copy(); + string appTestAssemblyPath = Path.Combine(app.Location, $"{testAssemblyName}.dll"); + File.WriteAllText(Path.Combine(app.Location, $"{testAssemblyName}.dll"), null); + + string expectedTestAssemblyPath = appWins + ? appTestAssemblyPath + : Path.Combine(SharedState.DotNetWithNetCoreApp.GreatestVersionSharedFxPath, $"{testAssemblyName}.dll"); + SharedState.DotNetWithNetCoreApp.Exec(Constants.AdditionalDeps.CommandLineArgument, additionalDependency.DepsJson, app.AppDll) + .EnableTracingAndCaptureOutputs() + .Execute() + .Should().Pass() + .And.HaveUsedAdditionalDeps(additionalDependency.DepsJson) + .And.HaveResolvedAssembly(expectedTestAssemblyPath); + } + } + } + public class ComponentPerAssemblyVersionResolution : PerAssemblyVersionResolutionBase, IClassFixture diff --git a/src/installer/tests/HostActivation.Tests/LightupAppActivation.cs b/src/installer/tests/HostActivation.Tests/LightupAppActivation.cs deleted file mode 100644 index acab6db71289e..0000000000000 --- a/src/installer/tests/HostActivation.Tests/LightupAppActivation.cs +++ /dev/null @@ -1,569 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.IO; -using System.Text.Json.Nodes; -using Xunit; - -namespace Microsoft.DotNet.CoreSetup.Test.HostActivation -{ - public class LightupAppActivation : IClassFixture, IDisposable - { - private SharedTestState sharedTestState; - - private const string SystemCollectionsImmutableFileVersion = "88.2.3.4"; - private const string SystemCollectionsImmutableAssemblyVersion = "88.0.1.2"; - - private readonly TestArtifact _baseDirArtifact; - private readonly string _builtSharedFxDir; - private readonly string _builtSharedUberFxDir; - private readonly string _fxBaseDir; - private readonly string _uberFxBaseDir; - - private TestProjectFixture GlobalLightupClientFixture; - - public LightupAppActivation(LightupAppActivation.SharedTestState fixture) - { - sharedTestState = fixture; - - // From the artifacts dir, it's possible to find where the sharedFrameworkPublish folder is. We need - // to locate it because we'll copy its contents into other folders - string artifactsDir = new RepoDirectoriesProvider().GetTestContextVariable("TEST_ARTIFACTS"); - string builtDotnet = Path.Combine(artifactsDir, "sharedFrameworkPublish"); - - // The dotnetLightupSharedFxLookup dir will contain some folders and files that will be necessary to perform the tests - string sharedLookupDir = Path.Combine(artifactsDir, "dotnetLightupSharedFxLookup"); - _baseDirArtifact = new TestArtifact(SharedFramework.CalculateUniqueTestDirectory(sharedLookupDir)); - _fxBaseDir = Path.Combine(_baseDirArtifact.Location, "shared", "Microsoft.NETCore.App"); - _uberFxBaseDir = Path.Combine(_baseDirArtifact.Location, "shared", "Microsoft.UberFramework"); - - SharedFramework.CopyDirectory(builtDotnet, _baseDirArtifact.Location); - - var repoDirectories = new RepoDirectoriesProvider(builtDotnet: _baseDirArtifact.Location); - GlobalLightupClientFixture = new TestProjectFixture("LightupClient", repoDirectories) - .EnsureRestored() - .BuildProject(); - - string greatestVersionSharedFxPath = sharedTestState.LightupLibFixture_Built.BuiltDotnet.GreatestVersionSharedFxPath; - string sharedFxVersion = (new DirectoryInfo(greatestVersionSharedFxPath)).Name; - _builtSharedFxDir = Path.Combine(builtDotnet, "shared", "Microsoft.NETCore.App", sharedFxVersion); - _builtSharedUberFxDir = Path.Combine(builtDotnet, "shared", "Microsoft.UberFramework", sharedFxVersion); - SharedFramework.CreateUberFrameworkArtifacts(_builtSharedFxDir, _builtSharedUberFxDir, SystemCollectionsImmutableAssemblyVersion, SystemCollectionsImmutableFileVersion); - } - - public void Dispose() - { - GlobalLightupClientFixture.Dispose(); - _baseDirArtifact.Dispose(); - } - - // Attempt to run the app with lightup deps.json specified but lightup library missing in the expected - // probe locations. - [Fact] - public void Muxer_activation_of_LightupApp_NoLightupLib_Fails() - { - var fixtureLib = sharedTestState.LightupLibFixture_Built - .Copy(); - - var fixtureApp = sharedTestState.LightupClientFixture - .Copy(); - - var dotnet = fixtureApp.BuiltDotnet; - var appDll = fixtureApp.TestProject.AppDll; - var libDepsJson = fixtureLib.TestProject.DepsJson; - - dotnet.Exec("exec", "--additional-deps", libDepsJson, appDll) - .CaptureStdErr() - .CaptureStdOut() - .Execute(expectedToFail: true) - .Should().Fail() - .And.HaveStdErrContaining( - "Error:" + Environment.NewLine + - " An assembly specified in the application dependencies manifest (LightupLib.deps.json) was not found:" + Environment.NewLine + - " package: \'LightupLib\', version: \'1.0.0\'" + Environment.NewLine + - " path: \'LightupLib.dll\'"); - } - - // Attempt to run the app with lightup deps.json specified and lightup library present in the expected - // probe locations. - [Fact] - public void Muxer_activation_of_LightupApp_WithLightupLib_Succeeds() - { - var fixtureLib = sharedTestState.LightupLibFixture_Published - .Copy(); - - var fixtureApp = sharedTestState.LightupClientFixture - .Copy(); - - var dotnet = fixtureApp.BuiltDotnet; - var appDll = fixtureApp.TestProject.AppDll; - var libDll = fixtureLib.TestProject.AppDll; - - // Get the version number of the SharedFX we just built since that is the version - // going to be specified in the test's runtimeconfig.json. - var builtSharedFXVersion = Path.GetFileName(dotnet.GreatestVersionSharedFxPath); - - // Create the M.N.App specific folder where lightup.deps.json can be found. - var baseDir = fixtureApp.TestProject.ProjectDirectory; - var customLightupPath = Path.Combine(baseDir, "shared"); - - // Delete any existing artifacts - if (Directory.Exists(customLightupPath)) - { - Directory.Delete(customLightupPath, true); - } - - customLightupPath = Path.Combine(customLightupPath, "Microsoft.NETCore.App"); - customLightupPath = Path.Combine(customLightupPath, builtSharedFXVersion); - - // Create the folder to which lightup.deps.json will be copied to. - Directory.CreateDirectory(customLightupPath); - - // Copy the lightup.deps.json - var libDepsJson = fixtureLib.TestProject.DepsJson; - File.Copy(libDepsJson, Path.Combine(customLightupPath, Path.GetFileName(libDepsJson))); - - // Copy the library to the location of the lightup app (app-local) - var destLibPath = Path.Combine(Path.GetDirectoryName(appDll), Path.GetFileName(libDll)); - File.Copy(libDll, destLibPath); - - // Execute the test using the custom lightup path where lightup.deps.json can be found. - dotnet.Exec("exec", "--additional-deps", baseDir, appDll) - .CaptureStdErr() - .CaptureStdOut() - .Execute() - .Should().Pass() - .And.HaveStdOutContaining("Hello LightupClient"); - } - - [Fact] - public void Muxer_activation_of_LightupApp_WithLightupLib_and_Roll_Backwards_From_Release_To_Release_Succeeds() - { - var fixtureLib = sharedTestState.LightupLibFixture_Published - .Copy(); - - var fixtureApp = GlobalLightupClientFixture - .Copy(); - - var dotnet = fixtureApp.BuiltDotnet; - var appDll = fixtureApp.TestProject.AppDll; - var libDepsJson = fixtureLib.TestProject.DepsJson; - - // Set desired version = 8888.0.0 - string runtimeConfig = Path.Combine(fixtureApp.TestProject.OutputDirectory, "LightupClient.runtimeconfig.json"); - SharedFramework.SetRuntimeConfigJson(runtimeConfig, "8888.0.0"); - - // Add versions in the exe folder - SharedFramework.AddAvailableSharedFxVersions(_builtSharedFxDir, _fxBaseDir, "8888.0.5"); - - CopyLightupLib(fixtureApp, fixtureLib); - - // Create the M.N.App specific folder where lightup.deps.json can be found. - var baseDir = fixtureApp.TestProject.ProjectDirectory; - var customLightupPath = Path.Combine(baseDir, "shared"); - - // Delete any existing artifacts - if (Directory.Exists(customLightupPath)) - { - Directory.Delete(customLightupPath, true); - } - - customLightupPath = Path.Combine(customLightupPath, "Microsoft.NETCore.App"); - - CreateLightupFolder(customLightupPath, $"8887.0.0", libDepsJson); - CreateLightupFolder(customLightupPath, $"8888.0.0", libDepsJson); - CreateLightupFolder(customLightupPath, $"8888.0.4-preview", libDepsJson); - - // Closest backwards patch version (selected) - CreateLightupFolder(customLightupPath, $"8888.0.4", libDepsJson); - string selectedLightupPath = Path.Combine(customLightupPath, "8888.0.4"); - - CreateLightupFolder(customLightupPath, $"8888.0.9", libDepsJson); - CreateLightupFolder(customLightupPath, $"8889.0.0", libDepsJson); - - // Version targeted: NetCoreApp 8888.0.0 - // Version existing: NetCoreApp 8888.0.5 - // Lightup folders: 8887.0.0 - // 8888.0.0 - // 8888.0.4-preview - // 8888.0.4 - // 8888.0.9 - // 8889.0.0 - // Expected: 8888.0.4 - dotnet.Exec("exec", "--additional-deps", baseDir, appDll) - .EnableTracingAndCaptureOutputs() - .Execute() - .Should().Pass() - .And.HaveStdOutContaining("Hello LightupClient") - .And.HaveStdErrContaining($"Using specified additional deps.json: '{selectedLightupPath}"); - } - - [Fact] - public void Muxer_activation_of_LightupApp_WithLightupLib_and_Roll_Backwards_From_Prerelease_To_Release_Succeeds() - { - var fixtureLib = sharedTestState.LightupLibFixture_Published - .Copy(); - - var fixtureApp = GlobalLightupClientFixture - .Copy(); - - var dotnet = fixtureApp.BuiltDotnet; - var appDll = fixtureApp.TestProject.AppDll; - var libDepsJson = fixtureLib.TestProject.DepsJson; - - // Set desired version = 8888.0.0 - string runtimeConfig = Path.Combine(fixtureApp.TestProject.OutputDirectory, "LightupClient.runtimeconfig.json"); - SharedFramework.SetRuntimeConfigJson(runtimeConfig, "8888.0.5-preview1"); - - // Add versions in the exe folder - SharedFramework.AddAvailableSharedFxVersions(_builtSharedFxDir, _fxBaseDir, "8888.0.5-preview2"); - - CopyLightupLib(fixtureApp, fixtureLib); - - // Create the M.N.App specific folder where lightup.deps.json can be found. - var baseDir = fixtureApp.TestProject.ProjectDirectory; - var customLightupPath = Path.Combine(baseDir, "shared"); - - // Delete any existing artifacts - if (Directory.Exists(customLightupPath)) - { - Directory.Delete(customLightupPath, true); - } - - customLightupPath = Path.Combine(customLightupPath, "Microsoft.NETCore.App"); - - CreateLightupFolder(customLightupPath, $"8888.0.0", libDepsJson); - CreateLightupFolder(customLightupPath, $"8888.0.4-preview", libDepsJson); - - // Closest backwards patch version (selected) - CreateLightupFolder(customLightupPath, $"8888.0.4", libDepsJson); - string selectedLightupPath = Path.Combine(customLightupPath, "8888.0.4"); - - CreateLightupFolder(customLightupPath, $"8888.0.5", libDepsJson); - - // Version targeted: NetCoreApp 8888.0.0-preview1 - // Version existing: NetCoreApp 8888.0.5-preview2 - // Lightup folders: 8888.0.0 - // 8888.0.4-preview - // 8888.0.4 - // 8888.0.5 - // Expected: 8888.0.4 - dotnet.Exec("exec", "--additional-deps", baseDir, appDll) - .EnableTracingAndCaptureOutputs() - .Execute() - .Should().Pass() - .And.HaveStdOutContaining("Hello LightupClient") - .And.HaveStdErrContaining($"Using specified additional deps.json: '{selectedLightupPath}"); - } - - [Fact] - public void Muxer_activation_of_LightupApp_WithLightupLib_and_Roll_Backwards_Fails() - { - var fixtureLib = sharedTestState.LightupLibFixture_Published - .Copy(); - - var fixtureApp = GlobalLightupClientFixture - .Copy(); - - var dotnet = fixtureApp.BuiltDotnet; - var appDll = fixtureApp.TestProject.AppDll; - var libDepsJson = fixtureLib.TestProject.DepsJson; - - // Set desired version = 8888.0.0 - string runtimeConfig = Path.Combine(fixtureApp.TestProject.OutputDirectory, "LightupClient.runtimeconfig.json"); - SharedFramework.SetRuntimeConfigJson(runtimeConfig, "8888.0.0"); - - // Add versions in the exe folder - SharedFramework.AddAvailableSharedFxVersions(_builtSharedFxDir, _fxBaseDir, "8888.0.1"); - - CopyLightupLib(fixtureApp, fixtureLib); - - // Create the M.N.App specific folder where lightup.deps.json can be found. - var baseDir = fixtureApp.TestProject.ProjectDirectory; - var customLightupPath = Path.Combine(baseDir, "shared"); - - // Delete any existing artifacts - if (Directory.Exists(customLightupPath)) - { - Directory.Delete(customLightupPath, true); - } - - customLightupPath = Path.Combine(customLightupPath, "Microsoft.NETCore.App"); - - CreateLightupFolder(customLightupPath, $"8887.0.0", libDepsJson); - CreateLightupFolder(customLightupPath, $"8889.0.0", libDepsJson); - - // Version targeted: NetCoreApp 8888.0.0 - // Version existing: NetCoreApp 8888.0.1 - // Lightup folders: 8887.0.0 - // 8889.0.0 - // Expected: fail since we only roll backward on patch, not minor - dotnet.Exec("exec", "--additional-deps", baseDir, appDll) - .EnableTracingAndCaptureOutputs() - .Execute(expectedToFail: true) - .Should().Fail() - .And.HaveStdErrContaining($"No additional deps directory less than or equal to [8888.0.1] found with same major and minor version."); - } - - // Attempt to run the app without lightup deps.json specified but lightup library present in the expected - // probe location (of being app-local). - [Fact] - public void Muxer_activation_of_LightupApp_WithLightupLib_NoLightupDepsJson_Fails() - { - var fixtureLib = sharedTestState.LightupLibFixture_Built - .Copy(); - - var fixtureApp = sharedTestState.LightupClientFixture - .Copy(); - - var dotnet = fixtureApp.BuiltDotnet; - var appDll = fixtureApp.TestProject.AppDll; - var libDll = fixtureLib.TestProject.AppDll; - - var destLibPath = Path.Combine(Path.GetDirectoryName(appDll), Path.GetFileName(libDll)); - - // Copy the library to the location of the lightup app - File.Copy(libDll, destLibPath); - - dotnet.Exec("exec", appDll) - .CaptureStdErr() - .CaptureStdOut() - .Execute(expectedToFail: true) - .Should().Fail() - .And.HaveStdOutContaining("Exception: Failed to load the lightup assembly!"); - } - - [Fact] - public void Additional_Deps_Lightup_Folder_With_Bad_JsonFile() - { - var fixture = GlobalLightupClientFixture - .Copy(); - - var fixtureLib = sharedTestState.LightupLibFixture_Published - .Copy(); - - CopyLightupLib(fixture, fixtureLib); - - var dotnet = fixture.BuiltDotnet; - var appDll = fixture.TestProject.AppDll; - - // Add version in the exe folder - SharedFramework.AddAvailableSharedFxVersions(_builtSharedFxDir, _fxBaseDir, "9999.0.0"); - - // Set desired version = 9999.0.0 - string runtimeConfig = Path.Combine(fixture.TestProject.OutputDirectory, "LightupClient.runtimeconfig.json"); - SharedFramework.SetRuntimeConfigJson(runtimeConfig, "9999.0.0"); - - string additionalDepsRootPath = Path.Combine(_fxBaseDir, "additionalDeps"); - - // Create a deps.json file in the folder "additionalDeps\shared\Microsoft.NETCore.App\9999.0.0" - string additionalDepsPath = Path.Combine(additionalDepsRootPath, "shared", "Microsoft.NETCore.App", "9999.0.0", "myAdditionalDeps.deps.json"); - FileInfo additionalDepsFile = new FileInfo(additionalDepsPath); - additionalDepsFile.Directory.Create(); - File.WriteAllText(additionalDepsFile.FullName, "THIS IS A BAD JSON FILE"); - - // Expected: a parsing error since the json file is bad. - dotnet.Exec("exec", "--additional-deps", additionalDepsRootPath, appDll) - .EnableTracingAndCaptureOutputs() - .Execute(expectedToFail: true) - .Should().Fail() - .And.HaveStdErrContaining($"Error initializing the dependency resolver: An error occurred while parsing: {additionalDepsPath}"); - } - - [Fact] - public void SharedFx_With_Higher_Version_Wins_Against_Additional_Deps() - { - var fixture = GlobalLightupClientFixture - .Copy(); - - var fixtureLib = sharedTestState.LightupLibFixture_Published - .Copy(); - - CopyLightupLib(fixture, fixtureLib); - - var dotnet = fixture.BuiltDotnet; - var appDll = fixture.TestProject.AppDll; - - // Set desired version = 7777.0.0 - string runtimeConfig = Path.Combine(fixture.TestProject.OutputDirectory, "LightupClient.runtimeconfig.json"); - SharedFramework.SetRuntimeConfigJson(runtimeConfig, "7777.0.0", null, useUberFramework: true); - - // Add versions in the exe folder - SharedFramework.AddAvailableSharedFxVersions(_builtSharedFxDir, _fxBaseDir, "9999.0.0"); - SharedFramework.AddAvailableSharedUberFxVersions(_builtSharedUberFxDir, _uberFxBaseDir, "9999.0.0", "7777.0.0"); - - // Copy NetCoreApp's copy of the assembly to the app location - string netcoreAssembly = Path.Combine(_fxBaseDir, "9999.0.0", "System.Collections.Immutable.dll"); - string appAssembly = Path.Combine(fixture.TestProject.OutputDirectory, "System.Collections.Immutable.dll"); - File.Copy(netcoreAssembly, appAssembly); - - // Create a deps.json file in the folder "additionalDeps\shared\Microsoft.NETCore.App\9999.0.0" - string additionalDepsRootPath = Path.Combine(_fxBaseDir, "additionalDeps"); - JsonObject versionInfo = new JsonObject - { - ["assemblyVersion"] = "0.0.0.1", - ["fileVersion"] = "0.0.0.2" - }; - string additionalDepsPath = CreateAdditionalDeps(additionalDepsRootPath, versionInfo); - - // Version: NetCoreApp 9999.0.0 - // UberFramework 7777.0.0 - // Existing:NetCoreApp 9999.0.0 - // UberFramework 7777.0.0 - // Expected: 9999.0.0 - // 7777.0.0 - // Expected: the uber framework's version of System.Collections.Immutable is used instead of the additional-deps - string uberAssembly = Path.Combine(_uberFxBaseDir, "7777.0.0", "System.Collections.Immutable.dll"); - dotnet.Exec("exec", "--additional-deps", additionalDepsPath, appDll) - .EnableTracingAndCaptureOutputs() - .Execute() - .Should().Pass() - .And.HaveStdErrContaining($"Using specified additional deps.json: '{additionalDepsPath}'") - .And.HaveStdErrContaining($"Adding tpa entry: {uberAssembly}") - .And.HaveStdErrContaining($"Adding tpa entry: {appAssembly}") - .And.HaveStdErrContaining($"Replacing deps entry [{appAssembly}") - .And.HaveStdErrContaining($"with [{uberAssembly}, AssemblyVersion:{SystemCollectionsImmutableAssemblyVersion}, FileVersion:{SystemCollectionsImmutableFileVersion}]") - // Verify final selection in TRUSTED_PLATFORM_ASSEMBLIES - .And.HaveStdErrContaining($"{uberAssembly}{Path.PathSeparator}") - .And.NotHaveStdErrContaining($"{appAssembly}{Path.PathSeparator}"); - } - - [Fact] - public void SharedFx_With_Lower_Version_Loses_Against_Additional_Deps() - { - var fixture = GlobalLightupClientFixture - .Copy(); - - var fixtureLib = sharedTestState.LightupLibFixture_Published - .Copy(); - - CopyLightupLib(fixture, fixtureLib); - - var dotnet = fixture.BuiltDotnet; - var appDll = fixture.TestProject.AppDll; - - // Set desired version = 7777.0.0 - string runtimeConfig = Path.Combine(fixture.TestProject.OutputDirectory, "LightupClient.runtimeconfig.json"); - SharedFramework.SetRuntimeConfigJson(runtimeConfig, "7777.0.0", null, useUberFramework: true); - - // Add versions in the exe folder - SharedFramework.AddAvailableSharedFxVersions(_builtSharedFxDir, _fxBaseDir, "9999.0.0"); - SharedFramework.AddAvailableSharedUberFxVersions(_builtSharedUberFxDir, _uberFxBaseDir, "9999.0.0", "7777.0.0"); - - // Copy NetCoreApp's copy of the assembly to the app location - string netcoreAssembly = Path.Combine(_fxBaseDir, "9999.0.0", "System.Collections.Immutable.dll"); - string appAssembly = Path.Combine(fixture.TestProject.OutputDirectory, "System.Collections.Immutable.dll"); - File.Copy(netcoreAssembly, appAssembly); - - // Create a deps.json file in the folder "additionalDeps\shared\Microsoft.NETCore.App\9999.0.0" - string additionalDepsRootPath = Path.Combine(_fxBaseDir, "additionalDeps"); - JsonObject versionInfo = new JsonObject - { - // Use Higher version numbers to win - ["assemblyVersion"] = "99.9.9.9", - ["fileVersion"] = "98.9.9.9" - }; - string additionalDepsPath = CreateAdditionalDeps(additionalDepsRootPath, versionInfo); - - // Version: NetCoreApp 9999.0.0 - // UberFramework 7777.0.0 - // Existing:NetCoreApp 9999.0.0 - // UberFramework 7777.0.0 - // Expected: 9999.0.0 - // 7777.0.0 - // Expected: the additional dep's version of System.Collections.Immutable is used instead of the uber's assembly - string uberAssembly = Path.Combine(_uberFxBaseDir, "7777.0.0", "System.Collections.Immutable.dll"); - dotnet.Exec("exec", "--additional-deps", additionalDepsPath, appDll) - .EnableTracingAndCaptureOutputs() - .Execute() - .Should().Pass() - .And.HaveStdErrContaining($"Using specified additional deps.json: '{additionalDepsPath}'") - .And.HaveStdErrContaining($"Adding tpa entry: {appAssembly}, AssemblyVersion: 99.9.9.9, FileVersion: 98.9.9.9") - // Verify final selection in TRUSTED_PLATFORM_ASSEMBLIES - .And.HaveStdErrContaining($"{appAssembly}{Path.PathSeparator}") - .And.NotHaveStdErrContaining($"{uberAssembly}{Path.PathSeparator}"); - } - - private static void CreateLightupFolder(string customLightupPath, string version, string libDepsJson) - { - customLightupPath = Path.Combine(customLightupPath, version); - - // Create the folder to which lightup.deps.json will be copied to. - Directory.CreateDirectory(customLightupPath); - - // Copy the lightup.deps.json - File.Copy(libDepsJson, Path.Combine(customLightupPath, Path.GetFileName(libDepsJson))); - } - - private static string CreateAdditionalDeps(string destDir, JsonObject immutableCollectionVersionInfo) - { - DirectoryInfo dir = new DirectoryInfo(destDir); - if (dir.Exists) - { - dir.Delete(true); - } - - dir.Create(); - - JsonObject depsjson = SharedFramework.CreateDepsJson("Microsoft.NETCore.App", "LightupLib/1.0.0", "LightupLib"); - - string depsFile = Path.Combine(destDir, "My.deps.json"); - File.WriteAllText(depsFile, depsjson.ToString()); - - SharedFramework.AddReferenceToDepsJson(depsFile, "LightupLib/1.0.0", "System.Collections.Immutable", "1.0.0", immutableCollectionVersionInfo); - SharedFramework.AddReferenceToDepsJson(depsFile, "LightupLib/1.0.0", "Newtonsoft.Json", "13.0.1"); - - return depsFile; - } - - private static void CopyLightupLib(TestProjectFixture fixtureApp, TestProjectFixture fixtureLib) - { - var appDll = fixtureApp.TestProject.AppDll; - var libDll = fixtureLib.TestProject.AppDll; - - // Copy the library to the location of the lightup app (app-local) - var destLibPath = Path.Combine(Path.GetDirectoryName(appDll), Path.GetFileName(libDll)); - File.Copy(libDll, destLibPath); - - // Copy the newtonsoft dependency to the location of the lightup app (app-local) - var srcNewtonsoftPath = Path.Combine(Path.GetDirectoryName(libDll), "Newtonsoft.Json.dll"); - var destNewtonsoftPath = Path.Combine(Path.GetDirectoryName(appDll), "Newtonsoft.Json.dll"); - File.Copy(srcNewtonsoftPath, destNewtonsoftPath); - } - - public class SharedTestState : IDisposable - { - public TestProjectFixture LightupLibFixture_Built { get; } - public TestProjectFixture LightupLibFixture_Published { get; } - - public TestProjectFixture LightupClientFixture { get; } - - public RepoDirectoriesProvider RepoDirectories { get; } - - public SharedTestState() - { - RepoDirectories = new RepoDirectoriesProvider(); - - LightupLibFixture_Built = new TestProjectFixture("LightupLib", RepoDirectories) - .EnsureRestored() - .BuildProject(); - - LightupLibFixture_Published = new TestProjectFixture("LightupLib", RepoDirectories) - .EnsureRestored() - .PublishProject(); - - LightupClientFixture = new TestProjectFixture("LightupClient", RepoDirectories) - .EnsureRestored() - .BuildProject(); - } - - public void Dispose() - { - LightupLibFixture_Built.Dispose(); - LightupLibFixture_Published.Dispose(); - LightupClientFixture.Dispose(); - } - } - } -} diff --git a/src/installer/tests/TestUtils/Constants.cs b/src/installer/tests/TestUtils/Constants.cs index 78fe3e6ea118d..f7c8cbe391c96 100644 --- a/src/installer/tests/TestUtils/Constants.cs +++ b/src/installer/tests/TestUtils/Constants.cs @@ -45,6 +45,11 @@ public static class FxVersion public const string CommandLineArgument = "--fx-version"; } + public static class AdditionalDeps + { + public const string CommandLineArgument = "--additional-deps"; + } + public static class RollForwardToPreRelease { public const string EnvironmentVariable = "DOTNET_ROLL_FORWARD_TO_PRERELEASE"; diff --git a/src/installer/tests/TestUtils/SharedFramework.cs b/src/installer/tests/TestUtils/SharedFramework.cs index f30fa8fa578e7..927389feec463 100644 --- a/src/installer/tests/TestUtils/SharedFramework.cs +++ b/src/installer/tests/TestUtils/SharedFramework.cs @@ -3,7 +3,6 @@ using System.IO; using System.Linq; -using System.Text.Json; using System.Text.Json.Nodes; using System.Threading; @@ -36,97 +35,6 @@ public static string CalculateUniqueTestDirectory(string baseDir) return dir; } - // This method adds a list of new framework version folders in the specified - // sharedFxBaseDir. The files are copied from the _buildSharedFxDir. - // Remarks: - // - If the sharedFxBaseDir does not exist, then a DirectoryNotFoundException - // is thrown. - // - If a specified version folder already exists, then it is deleted and replaced - // with the contents of the _builtSharedFxDir. - public static void AddAvailableSharedFxVersions(string sharedFxDir, string sharedFxBaseDir, params string[] availableVersions) - { - DirectoryInfo sharedFxBaseDirInfo = new DirectoryInfo(sharedFxBaseDir); - - if (!sharedFxBaseDirInfo.Exists) - { - throw new DirectoryNotFoundException(); - } - - foreach (string version in availableVersions) - { - string newSharedFxDir = Path.Combine(sharedFxBaseDir, version); - CopyDirectory(sharedFxDir, newSharedFxDir); - } - } - - // This method adds a list of new framework version folders in the specified - // sharedFxUberBaseDir. A runtimeconfig file is created that references - // Microsoft.NETCore.App version=sharedFxBaseVersion - public static void AddAvailableSharedUberFxVersions(string sharedFxDir, string sharedUberFxBaseDir, string sharedFxBaseVersion, params string[] availableUberVersions) - { - DirectoryInfo sharedFxUberBaseDirInfo = new DirectoryInfo(sharedUberFxBaseDir); - - if (!sharedFxUberBaseDirInfo.Exists) - { - sharedFxUberBaseDirInfo.Create(); - } - - foreach (string version in availableUberVersions) - { - string newSharedFxDir = Path.Combine(sharedUberFxBaseDir, version); - CopyDirectory(sharedFxDir, newSharedFxDir); - - string runtimeBaseConfig = Path.Combine(newSharedFxDir, "Microsoft.UberFramework.runtimeconfig.json"); - SharedFramework.SetRuntimeConfigJson(runtimeBaseConfig, sharedFxBaseVersion, null); - } - } - - // Generated json file: - /* - * { - * "runtimeOptions": { - * "framework": { - * "name": "Microsoft.NETCore.App", - * "version": {version} - * }, - * "rollForwardOnNoCandidateFx": {rollFwdOnNoCandidateFx} <-- only if rollFwdOnNoCandidateFx is defined - * } - * } - */ - public static void SetRuntimeConfigJson(string destFile, string version, int? rollFwdOnNoCandidateFx = null, bool? useUberFramework = false, JsonArray frameworks = null) - { - string name = useUberFramework.HasValue && useUberFramework.Value ? "Microsoft.UberFramework" : "Microsoft.NETCore.App"; - - JsonObject runtimeOptions = new JsonObject - { - ["framework"] = new JsonObject - { - ["name"] = name, - ["version"] = version - } - }; - - if (rollFwdOnNoCandidateFx.HasValue) - { - runtimeOptions.Add("rollForwardOnNoCandidateFx", rollFwdOnNoCandidateFx); - } - - if (frameworks != null) - { - runtimeOptions.Add("frameworks", frameworks); - } - - FileInfo file = new FileInfo(destFile); - if (!file.Directory.Exists) - { - file.Directory.Create(); - } - - JsonObject json = new JsonObject(); - json.Add("runtimeOptions", runtimeOptions); - File.WriteAllText(destFile, json.ToString()); - } - // CopyDirectory recursively copies a directory // Remarks: // - If the dest dir does not exist, then it is created. @@ -165,109 +73,11 @@ public static void CopyDirectory(string srcDir, string dstDir) } } - public static void CreateUberFrameworkArtifacts(string builtSharedFxDir, string builtSharedUberFxDir, string assemblyVersion = null, string fileVersion = null) - { - DirectoryInfo dir = new DirectoryInfo(builtSharedUberFxDir); - if (dir.Exists) - { - dir.Delete(true); - } - - dir.Create(); - - JsonObject versionInfo = new JsonObject(); - if (assemblyVersion != null) - { - versionInfo.Add("assemblyVersion", (JsonNode)assemblyVersion); - } - - if (fileVersion != null) - { - versionInfo.Add("fileVersion", (JsonNode)fileVersion); - } - - JsonObject depsjson = CreateDepsJson("UberFx", "System.Collections.Immutable/1.0.0", "System.Collections.Immutable", versionInfo); - string depsFile = Path.Combine(builtSharedUberFxDir, "Microsoft.UberFramework.deps.json"); - File.WriteAllText(depsFile, depsjson.ToString()); - - // Copy the test assembly - string fileSource = Path.Combine(builtSharedFxDir, "System.Collections.Immutable.dll"); - string fileDest = Path.Combine(builtSharedUberFxDir, "System.Collections.Immutable.dll"); - File.Copy(fileSource, fileDest); - } - - public static JsonObject CreateDepsJson(string fxName, string testPackage, string testAssembly, JsonObject versionInfo = null) - { - // Create the deps.json. Generated file (example) - /* - { - "runtimeTarget": { - "name": "UberFx" - }, - "targets": { - "UberFx": { - "System.Collections.Immutable/1.0.0": { - "dependencies": {} - "runtime": { - "System.Collections.Immutable.dll": {} - } - } - } - }, - "libraries": { - "System.Collections.Immutable/1.0.0": { - "type": "assemblyreference", - "serviceable": false, - "sha512": "" - } - } - } - */ - - if (versionInfo == null) - { - versionInfo = new JsonObject(); - } - - JsonObject depsjson = new JsonObject - { - ["runtimeTarget"] = new JsonObject - { - ["name"] = fxName - }, - ["targets"] = new JsonObject - { - [fxName] = new JsonObject - { - [testPackage] = new JsonObject - { - ["dependencies"] = new JsonObject(), - ["runtime"] = new JsonObject - { - [testAssembly + ".dll"] = versionInfo - } - } - } - }, - ["libraries"] = new JsonObject - { - [testPackage] = new JsonObject - { - ["type"] = "assemblyreference", - ["serviceable"] = false, - ["sha512"] = "" - } - } - }; - - return depsjson; - } - public static void AddReferenceToDepsJson( - string jsonFile, - string fxNameWithVersion, - string testPackage, - string testPackageVersion, + string jsonFile, + string fxNameWithVersion, + string testPackage, + string testPackageVersion, JsonObject testAssemblyVersionInfo = null, string testAssembly = null) {