From 4cd5a0b4fa2112a9e65c894ba5a5fc11d9c64301 Mon Sep 17 00:00:00 2001 From: vitek-karas <10670590+vitek-karas@users.noreply.github.com> Date: Tue, 22 Feb 2022 08:20:11 -0800 Subject: [PATCH 1/4] Refactor and improve MLL tests * Remove duplicated code - I know that tests should be descriptive, but repeating 100 times that we want to capture output doesn't feel necessary. * For some of them move more stuff into the shared test state - this improves perf as we avoid repeating the setup (copying files around) for each test case, we do it once for the entire class I also changed some of the tests to "Theory" as it's easier to read that way. * `SDKLookup.cs` - moved most of the state in to the shared state to speed up the tests. * Adding more cases into the "theory" tests * Most notably for the framework resolution I added variations on the TFM (which will be needed when we implement disable of MLL) * Adding new tests mostly around "list runtimes" (various forms), "list sdks" (various forms) and errors (which also list runtimes or sdks) * Ported all of the `MultiLevelLookupFramework` tests over to the `FrameworkResolution` and `DependencyResolutions` suites which have a more robust test infra and can run the same tests much faster. Along the way I added lot more variations on top of the existing tests: * `PerAssemblyVersionResolutionMultipleFrameworks.cs` - this is actually not an MLL test, but I moved it to the new infra to make it much faster * `MultipleHives.cs` - MLL framework resolution tests For SDK resolution I kept the `MultiLevelSDKLookup.cs` just removed code duplication and added new variants. For the core reviewers: I promise I didn't remove any single test case in spirit with these exceptions: * We had tests which validated that framework resolution is not affected by frameworks in current directory and also by frameworks in the user's directory. I left some basic test for the current directory check, but I completely removed the user's directory variant as the product simply doesn't have any code around that anymore. --- .../ComponentDependencyResolutionBase.cs | 15 +- .../PerAssemblyVersionResolution.cs | 13 +- ...mblyVersionResolutionMultipleFrameworks.cs | 200 +++++++ .../FrameworkResolutionBase.cs | 30 +- ...meworkResolutionCommandResultExtensions.cs | 17 +- .../FrameworkResolution/MultipleHives.cs | 245 ++++++-- .../MultilevelSDKLookup.cs | 361 ++++-------- .../MultilevelSharedFxLookup.DepsVersion.cs | 130 ----- .../MultilevelSharedFxLookup.cs | 347 ----------- .../tests/HostActivation.Tests/SDKLookup.cs | 546 ++++++------------ src/installer/tests/TestUtils/Command.cs | 6 + .../tests/TestUtils/CommandExtensions.cs | 7 +- src/installer/tests/TestUtils/Constants.cs | 10 + .../tests/TestUtils/DotNetBuilder.cs | 16 +- .../tests/TestUtils/RuntimeConfig.cs | 14 + .../test/mockhostpolicy/mockhostpolicy.cpp | 6 +- 16 files changed, 813 insertions(+), 1150 deletions(-) create mode 100644 src/installer/tests/HostActivation.Tests/DependencyResolution/PerAssemblyVersionResolutionMultipleFrameworks.cs delete mode 100644 src/installer/tests/HostActivation.Tests/MultilevelSharedFxLookup.DepsVersion.cs delete mode 100644 src/installer/tests/HostActivation.Tests/MultilevelSharedFxLookup.cs diff --git a/src/installer/tests/HostActivation.Tests/DependencyResolution/ComponentDependencyResolutionBase.cs b/src/installer/tests/HostActivation.Tests/DependencyResolution/ComponentDependencyResolutionBase.cs index c87c28ac4a982c..289c48a3c0f288 100644 --- a/src/installer/tests/HostActivation.Tests/DependencyResolution/ComponentDependencyResolutionBase.cs +++ b/src/installer/tests/HostActivation.Tests/DependencyResolution/ComponentDependencyResolutionBase.cs @@ -26,11 +26,12 @@ public abstract class ComponentSharedTestStateBase : SharedTestStateBase public ComponentSharedTestStateBase() { - DotNetWithNetCoreApp = DotNet("WithNetCoreApp") - .AddMicrosoftNETCoreAppFrameworkMockCoreClr("4.0.0", builder => CustomizeDotNetWithNetCoreAppMicrosoftNETCoreApp(builder)) - .Build(); + var dotNetBuilder = DotNet("WithNetCoreApp") + .AddMicrosoftNETCoreAppFrameworkMockCoreClr("4.0.0", builder => CustomizeDotNetWithNetCoreAppMicrosoftNETCoreApp(builder)); + CustomizeDotNetWithNetCoreApp(dotNetBuilder); + DotNetWithNetCoreApp = dotNetBuilder.Build(); - TestApp app = CreateFrameworkReferenceApp(MicrosoftNETCoreApp, "4.0.0"); + TestApp app = CreateTestFrameworkReferenceApp(); FrameworkReferenceApp = NetCoreAppBuilder.PortableForNETCoreApp(app) .WithProject(p => p.WithAssemblyGroup(null, g => g.WithMainAssembly())) .Build(app); @@ -38,10 +39,16 @@ public ComponentSharedTestStateBase() _nativeHostingState = new NativeHosting.SharedTestStateBase(); } + protected virtual TestApp CreateTestFrameworkReferenceApp() => CreateFrameworkReferenceApp(MicrosoftNETCoreApp, "4.0.0"); + protected virtual void CustomizeDotNetWithNetCoreAppMicrosoftNETCoreApp(NetCoreAppBuilder builder) { } + protected virtual void CustomizeDotNetWithNetCoreApp(DotNetBuilder builder) + { + } + public CommandResult RunComponentResolutionTest(TestApp component, Action commandCustomizer = null) { return RunComponentResolutionTest(component.AppDll, FrameworkReferenceApp, DotNetWithNetCoreApp.GreatestVersionHostFxrPath, commandCustomizer); diff --git a/src/installer/tests/HostActivation.Tests/DependencyResolution/PerAssemblyVersionResolution.cs b/src/installer/tests/HostActivation.Tests/DependencyResolution/PerAssemblyVersionResolution.cs index ea21df856ee69b..5140a572c7a640 100644 --- a/src/installer/tests/HostActivation.Tests/DependencyResolution/PerAssemblyVersionResolution.cs +++ b/src/installer/tests/HostActivation.Tests/DependencyResolution/PerAssemblyVersionResolution.cs @@ -57,15 +57,10 @@ public PerAssemblyVersionResolutionBase(SharedTestState fixture) [InlineData(TestAssemblyWithFileVersion, null, "3.2.2.2", false)] public void AppWithSameAssemblyAsFramework(string testAssemblyName, string appAsmVersion, string appFileVersion, bool appWins) { - RunTest(b => b - .WithPackage(TestVersionsPackage, "1.0.0", lib => lib - .WithAssemblyGroup(null, g => g - .WithAsset(testAssemblyName + ".dll", rf => rf - .WithVersion(appAsmVersion, appFileVersion)))), - testAssemblyName, appAsmVersion, appFileVersion, appWins); + RunTest(testAssemblyName, appAsmVersion, appFileVersion, appWins); } - protected abstract void RunTest(Action customizer, string testAssemblyName, string appAsmVersion, string appFileVersion, bool appWins); + protected abstract void RunTest(string testAssemblyName, string appAsmVersion, string appFileVersion, bool appWins); public class SharedTestState : ComponentSharedTestStateBase { @@ -105,7 +100,7 @@ public AppPerAssemblyVersionResolution(SharedTestState sharedState) { } - protected override void RunTest(Action customizer, string testAssemblyName, string appAsmVersion, string appFileVersion, bool appWins) + protected override void RunTest(string testAssemblyName, string appAsmVersion, string appFileVersion, bool appWins) { var app = SharedState.CreateTestFrameworkReferenceApp(b => b .WithPackage(TestVersionsPackage, "1.0.0", lib => lib @@ -133,7 +128,7 @@ public ComponentPerAssemblyVersionResolution(SharedTestState sharedState) { } - protected override void RunTest(Action customizer, string testAssemblyName, string appAsmVersion, string appFileVersion, bool appWins) + protected override void RunTest(string testAssemblyName, string appAsmVersion, string appFileVersion, bool appWins) { var component = SharedState.CreateComponentWithNoDependencies(b => b .WithPackage(TestVersionsPackage, "1.0.0", lib => lib diff --git a/src/installer/tests/HostActivation.Tests/DependencyResolution/PerAssemblyVersionResolutionMultipleFrameworks.cs b/src/installer/tests/HostActivation.Tests/DependencyResolution/PerAssemblyVersionResolutionMultipleFrameworks.cs new file mode 100644 index 00000000000000..428fc60dbe8853 --- /dev/null +++ b/src/installer/tests/HostActivation.Tests/DependencyResolution/PerAssemblyVersionResolutionMultipleFrameworks.cs @@ -0,0 +1,200 @@ +// 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 Xunit; + +namespace Microsoft.DotNet.CoreSetup.Test.HostActivation.DependencyResolution +{ + public abstract class PerAssemblyVersionResolutionMultipleFrameworksBase : + ComponentDependencyResolutionBase, + IClassFixture + { + protected readonly SharedTestState SharedState; + + public PerAssemblyVersionResolutionMultipleFrameworksBase(SharedTestState fixture) + { + SharedState = fixture; + } + + protected const string HighWare = "HighWare"; + protected const string TestVersionsPackage = "Test.Versions.Package"; + + // The test framework above has 4 assemblies in it each with different set of assembly and file versions. + // NetCoreApp HighWare + // - TestAssemblyWithNoVersions: null , null null , null + // - TestAssemblyWithAssemblyVersion: 2.1.1.1, null 2.1.1.2, null + // - TestAssemblyWithFileVersion: null , 3.2.2.2 null , 3.2.2.2 + // - TestAssemblyWithBothVersions: 2.1.1.1, 3.2.2.2 2.1.1.0, 3.2.2.0 + private const string TestAssemblyWithNoVersions = "Test.Assembly.NoVersions"; + private const string TestAssemblyWithAssemblyVersion = "Test.Assembly.AssemblyVersion"; + private const string TestAssemblyWithFileVersion = "Test.Assembly.FileVersion"; + private const string TestAssemblyWithBothVersions = "Test.Assembly.BothVersions"; + + [Theory] + [InlineData(TestAssemblyWithBothVersions, null, null, MicrosoftNETCoreApp)] // NetCoreApp has higher version than HighWare + [InlineData(TestAssemblyWithBothVersions, "1.0.0.0", "1.0.0.0", MicrosoftNETCoreApp)] + [InlineData(TestAssemblyWithBothVersions, "3.0.0.0", "4.0.0.0", null)] // App has higher version than any framework + [InlineData(TestAssemblyWithBothVersions, "2.1.1.1", "3.3.0.0", null)] // App has higher file version + [InlineData(TestAssemblyWithBothVersions, "2.1.1.1", "3.2.2.2", MicrosoftNETCoreApp)] // Lower level framework always wins on equality (this is intentional) + [InlineData(TestAssemblyWithBothVersions, null, "4.0.0.0", MicrosoftNETCoreApp)] // The one with version wins + [InlineData(TestAssemblyWithBothVersions, null, "2.0.0.0", MicrosoftNETCoreApp)] // The one with version wins + [InlineData(TestAssemblyWithBothVersions, "3.0.0.0", null, null)] + [InlineData(TestAssemblyWithBothVersions, "2.1.1.1", null, MicrosoftNETCoreApp)] + [InlineData(TestAssemblyWithNoVersions, null, null, MicrosoftNETCoreApp)] // No versions are treated as equal (so lower one wins) + [InlineData(TestAssemblyWithNoVersions, "1.0.0.0", null, null)] // The one with version wins + [InlineData(TestAssemblyWithNoVersions, "1.0.0.0", "1.0.0.0", null)] // The one with version wins + [InlineData(TestAssemblyWithNoVersions, null, "1.0.0.0", null)] // The one with version wins + [InlineData(TestAssemblyWithAssemblyVersion, null, null, HighWare)] // Highware has higher version than NetCoreApp + [InlineData(TestAssemblyWithAssemblyVersion, "1.0.0.0", null, HighWare)] + [InlineData(TestAssemblyWithAssemblyVersion, null, "1.0.0.0", HighWare)] + [InlineData(TestAssemblyWithAssemblyVersion, "3.0.0.0", "1.0.0.0", null)] // App has higher version than any framework + [InlineData(TestAssemblyWithAssemblyVersion, "2.1.1.2", null, HighWare)] // Both are exactly the same, so lower level wins + [InlineData(TestAssemblyWithAssemblyVersion, "2.1.1.2", "1.0.0.0", null)] + [InlineData(TestAssemblyWithFileVersion, null, null, MicrosoftNETCoreApp)] // Frameworks both have the same version - lower one wins + [InlineData(TestAssemblyWithFileVersion, "1.0.0.0", null, null)] // App has assembly version, no framework has it - so app wins + [InlineData(TestAssemblyWithFileVersion, null, "1.0.0.0", MicrosoftNETCoreApp)] + [InlineData(TestAssemblyWithFileVersion, null, "4.0.0.0", null)] // App has higher version than either framework + [InlineData(TestAssemblyWithFileVersion, null, "3.2.2.2", MicrosoftNETCoreApp)] // Exactly equal - lower one wins + public void AppWithSameAssemblyAsFramework(string testAssemblyName, string appAsmVersion, string appFileVersion, string frameWorkWins) + { + RunTest(null, testAssemblyName, appAsmVersion, appFileVersion, frameWorkWins); + } + + [Theory] + [InlineData("1.1.1")] // Exact match - no roll forward + [InlineData("1.1.0")] // Path roll forward + [InlineData("1.0.0")] // Minor + [InlineData("0.0.0")] // Major + public void AppWithExactlySameAssemblyAsFrameworkWithRollForward(string frameworkReferenceVersion) + { + RunTest( + runtimeConfig => runtimeConfig + .WithFramework(HighWare, frameworkReferenceVersion) + .WithRollForward(Constants.RollForwardSetting.Major), + TestAssemblyWithBothVersions, "2.1.1.1", "3.2.2.2", MicrosoftNETCoreApp); + } + + protected abstract void RunTest(Action runtimeConfigCustomizer, string testAssemblyName, string appAsmVersion, string appFileVersion, string frameWorkWins); + + public class SharedTestState : ComponentSharedTestStateBase + { + public SharedTestState() + { + } + + protected override TestApp CreateTestFrameworkReferenceApp() => CreateFrameworkReferenceApp(HighWare, "1.1.1"); + + + protected override void CustomizeDotNetWithNetCoreAppMicrosoftNETCoreApp(NetCoreAppBuilder builder) + { + builder + .WithPackage(TestVersionsPackage, "1.1.1", b => b + .WithAssemblyGroup(null, g => g + .WithAsset(TestAssemblyWithNoVersions + ".dll") + .WithAsset(TestAssemblyWithAssemblyVersion + ".dll", rf => rf.WithVersion("2.1.1.1", null)) + .WithAsset(TestAssemblyWithFileVersion + ".dll", rf => rf.WithVersion(null, "3.2.2.2")) + .WithAsset(TestAssemblyWithBothVersions + ".dll", rf => rf.WithVersion("2.1.1.1", "3.2.2.2")))); + } + + protected override void CustomizeDotNetWithNetCoreApp(DotNetBuilder builder) + { + builder.AddFramework( + HighWare, + "1.1.1", + runtimeConfig => runtimeConfig.WithFramework(MicrosoftNETCoreApp, "4.0.0"), + path => NetCoreAppBuilder.ForNETCoreApp(HighWare, RepoDirectories.TargetRID) + .WithProject(HighWare, "1.1.1", p => p + .WithAssemblyGroup(null, g => g + .WithAsset(TestAssemblyWithNoVersions + ".dll") + .WithAsset(TestAssemblyWithAssemblyVersion + ".dll", rf => rf.WithVersion("2.1.1.2", null)) + .WithAsset(TestAssemblyWithFileVersion + ".dll", rf => rf.WithVersion(null, "3.2.2.2")) + .WithAsset(TestAssemblyWithBothVersions + ".dll", rf => rf.WithVersion("2.1.1.0", "3.2.2.0")))) + .Build(new TestApp(path, HighWare))); + } + + public TestApp CreateTestFrameworkReferenceApp(Action customizer) + { + TestApp testApp = FrameworkReferenceApp.Copy(); + NetCoreAppBuilder builder = NetCoreAppBuilder.PortableForNETCoreApp(testApp); + builder.WithProject(p => p + .WithAssemblyGroup(null, g => g.WithMainAssembly())); + customizer(builder); + return builder.Build(testApp); + } + } + } + + public class AppPerAssemblyVersionResolutionMultipleFrameworks : + PerAssemblyVersionResolutionMultipleFrameworksBase, + IClassFixture + { + public AppPerAssemblyVersionResolutionMultipleFrameworks(SharedTestState sharedState) + : base(sharedState) + { + } + + protected override void RunTest(Action runtimeConfigCustomizer, string testAssemblyName, string appAsmVersion, string appFileVersion, string frameworkWins) + { + var app = SharedState.CreateTestFrameworkReferenceApp(b => b + .WithPackage(TestVersionsPackage, "1.0.0", lib => lib + .WithAssemblyGroup(null, g => g + .WithAsset(testAssemblyName + ".dll", rf => rf + .WithVersion(appAsmVersion, appFileVersion))))); + if (runtimeConfigCustomizer is not null) + { + var runtimeConfig = new RuntimeConfig(app.RuntimeConfigJson); + runtimeConfigCustomizer(runtimeConfig); + runtimeConfig.Save(); + } + + string expectedBaseLocation = frameworkWins switch + { + MicrosoftNETCoreApp => SharedState.DotNetWithNetCoreApp.GreatestVersionSharedFxPath, + HighWare => Path.Combine(SharedState.DotNetWithNetCoreApp.BinPath, "shared", HighWare, "1.1.1"), + _ => app.Location, + }; + string expectedTestAssemblyPath = Path.Combine(expectedBaseLocation, testAssemblyName + ".dll"); + + SharedState.DotNetWithNetCoreApp.Exec(app.AppDll) + .EnableTracingAndCaptureOutputs() + .Execute() + .Should().Pass() + .And.HaveResolvedAssembly(expectedTestAssemblyPath); + } + } + + public class ComponentPerAssemblyVersionResolutionMultipleFrameworks : + PerAssemblyVersionResolutionMultipleFrameworksBase, + IClassFixture + { + public ComponentPerAssemblyVersionResolutionMultipleFrameworks(SharedTestState sharedState) + : base(sharedState) + { + } + + protected override void RunTest(Action runtimeConfigCustomizer, string testAssemblyName, string appAsmVersion, string appFileVersion, string frameworkWins) + { + var component = SharedState.CreateComponentWithNoDependencies(b => b + .WithPackage(TestVersionsPackage, "1.0.0", lib => lib + .WithAssemblyGroup(null, g => g + .WithAsset(testAssemblyName + ".dll", rf => rf + .WithVersion(appAsmVersion, appFileVersion))))); + if (runtimeConfigCustomizer is not null) + { + var runtimeConfig = RuntimeConfig.FromFile(component.RuntimeConfigJson); + runtimeConfigCustomizer(runtimeConfig); + runtimeConfig.Save(); + } + + // For component dependency resolution, frameworks are not considered, so the assembly from the component always wins + string expectedTestAssemblyPath = Path.Combine(component.Location, testAssemblyName + ".dll"); + + SharedState.RunComponentResolutionTest(component) + .Should().Pass() + .And.HaveSuccessfullyResolvedComponentDependencies() + .And.HaveResolvedComponentDependencyAssembly($"{component.AppDll};{expectedTestAssemblyPath}"); + } + } +} diff --git a/src/installer/tests/HostActivation.Tests/FrameworkResolution/FrameworkResolutionBase.cs b/src/installer/tests/HostActivation.Tests/FrameworkResolution/FrameworkResolutionBase.cs index c36d0c0c8102bb..a68b31a3c410a5 100644 --- a/src/installer/tests/HostActivation.Tests/FrameworkResolution/FrameworkResolutionBase.cs +++ b/src/installer/tests/HostActivation.Tests/FrameworkResolution/FrameworkResolutionBase.cs @@ -1,11 +1,11 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using Microsoft.DotNet.Cli.Build; -using Microsoft.DotNet.Cli.Build.Framework; using System; using System.IO; using System.Linq; +using Microsoft.DotNet.Cli.Build; +using Microsoft.DotNet.Cli.Build.Framework; namespace Microsoft.DotNet.CoreSetup.Test.HostActivation.FrameworkResolution { @@ -24,18 +24,21 @@ protected CommandResult RunTest( TestApp app, TestSettings settings, Action resultAction = null, - bool multiLevelLookup = false) + bool? multiLevelLookup = false) { using (DotNetCliExtensions.DotNetCliCustomizer dotnetCustomizer = settings.DotnetCustomizer == null ? null : dotnet.Customize()) { settings.DotnetCustomizer?.Invoke(dotnetCustomizer); - if (settings.RuntimeConfigCustomizer != null) + if (app is not null) { - settings.RuntimeConfigCustomizer(RuntimeConfig.Path(app.RuntimeConfigJson)).Save(); - } + if (settings.RuntimeConfigCustomizer != null) + { + settings.RuntimeConfigCustomizer(RuntimeConfig.Path(app.RuntimeConfigJson)).Save(); + } - settings.WithCommandLine(app.AppDll); + settings.WithCommandLine(app.AppDll); + } Command command = dotnet.Exec(settings.CommandLine.First(), settings.CommandLine.Skip(1).ToArray()); @@ -156,9 +159,18 @@ public TestApp CreateSelfContainedAppWithMockHostPolicy() public void Dispose() { - if (!TestArtifact.PreserveTestRuns() && Directory.Exists(_baseDir)) + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (disposing) { - Directory.Delete(_baseDir, true); + if (!TestArtifact.PreserveTestRuns() && Directory.Exists(_baseDir)) + { + Directory.Delete(_baseDir, true); + } } } } diff --git a/src/installer/tests/HostActivation.Tests/FrameworkResolution/FrameworkResolutionCommandResultExtensions.cs b/src/installer/tests/HostActivation.Tests/FrameworkResolution/FrameworkResolutionCommandResultExtensions.cs index a0b6feda95774d..017a601e994910 100644 --- a/src/installer/tests/HostActivation.Tests/FrameworkResolution/FrameworkResolutionCommandResultExtensions.cs +++ b/src/installer/tests/HostActivation.Tests/FrameworkResolution/FrameworkResolutionCommandResultExtensions.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.IO; using FluentAssertions; using Microsoft.DotNet.Cli.Build.Framework; @@ -8,15 +9,19 @@ namespace Microsoft.DotNet.CoreSetup.Test.HostActivation.FrameworkResolution { internal static class FrameworkResolutionCommandResultExtensions { - public static AndConstraint HaveResolvedFramework(this CommandResultAssertions assertion, string name, string version) + public static AndConstraint HaveResolvedFramework(this CommandResultAssertions assertion, string name, string version, string resolvedFrameworkBasePath = null) { - return assertion.HaveStdOutContaining($"mock frameworks: {name} {version}"); + string expectedOutput = $"mock frameworks: {name} {version}"; + if (resolvedFrameworkBasePath is not null) + expectedOutput += $" [path: {Path.Combine(resolvedFrameworkBasePath, "shared", name, version)}]"; + + return assertion.HaveStdOutContaining(expectedOutput); } - public static AndConstraint ShouldHaveResolvedFramework(this CommandResult result, string resolvedFrameworkName, string resolvedFrameworkVersion) + public static AndConstraint ShouldHaveResolvedFramework(this CommandResult result, string resolvedFrameworkName, string resolvedFrameworkVersion, string resolvedFrameworkBasePath = null) { return result.Should().Pass() - .And.HaveResolvedFramework(resolvedFrameworkName, resolvedFrameworkVersion); + .And.HaveResolvedFramework(resolvedFrameworkName, resolvedFrameworkVersion, resolvedFrameworkBasePath); } /// @@ -28,7 +33,7 @@ public static AndConstraint ShouldHaveResolvedFramework /// Either null in which case the command result is expected to fail and not find compatible framework version, /// or the framework versions in which case the command result is expected to succeed and resolve the specified framework version. /// Constraint - public static AndConstraint ShouldHaveResolvedFrameworkOrFailToFind(this CommandResult result, string resolvedFrameworkName, string resolvedFrameworkVersion) + public static AndConstraint ShouldHaveResolvedFrameworkOrFailToFind(this CommandResult result, string resolvedFrameworkName, string resolvedFrameworkVersion, string resolvedFrameworkBasePath = null) { if (resolvedFrameworkName == null || resolvedFrameworkVersion == null || resolvedFrameworkVersion == FrameworkResolutionBase.ResolvedFramework.NotFound) @@ -37,7 +42,7 @@ public static AndConstraint ShouldHaveResolvedFramework } else { - return result.ShouldHaveResolvedFramework(resolvedFrameworkName, resolvedFrameworkVersion); + return result.ShouldHaveResolvedFramework(resolvedFrameworkName, resolvedFrameworkVersion, resolvedFrameworkBasePath); } } diff --git a/src/installer/tests/HostActivation.Tests/FrameworkResolution/MultipleHives.cs b/src/installer/tests/HostActivation.Tests/FrameworkResolution/MultipleHives.cs index 0a12cd2640ce9b..014f98dd70d938 100644 --- a/src/installer/tests/HostActivation.Tests/FrameworkResolution/MultipleHives.cs +++ b/src/installer/tests/HostActivation.Tests/FrameworkResolution/MultipleHives.cs @@ -1,10 +1,12 @@ // 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.Collections.Generic; +using System.IO; +using System.Linq; using Microsoft.DotNet.Cli.Build; using Microsoft.DotNet.Cli.Build.Framework; -using System; -using System.Runtime.InteropServices; using Xunit; namespace Microsoft.DotNet.CoreSetup.Test.HostActivation.FrameworkResolution @@ -20,56 +22,211 @@ public MultipleHives(SharedTestState sharedState) SharedState = sharedState; } - [Fact] - [PlatformSpecific(TestPlatforms.Windows)] // Multiple hives are only supported on Windows. - public void FrameworkHiveSelection_GlobalHiveWithBetterMatch() + [Theory] + // MLL where global hive has a better match + [InlineData("5.0.0", "net5.0", true, "5.1.2")] + [InlineData("5.0.0", "net5.0", null, "5.1.2")] // MLL is on by default, so same as true + [InlineData("5.0.0", "net5.0", false, "5.2.0")] // No global hive allowed + // MLL (where global hive has better match) with various TFMs + [InlineData("5.0.0", "netcoreapp3.0", true, "5.1.2")] + [InlineData("5.0.0", "netcoreapp3.0", null, "5.1.2")] + [InlineData("5.0.0", "netcoreapp3.0", false, "5.2.0")] + [InlineData("5.0.0", "netcoreapp3.1", true, "5.1.2")] + [InlineData("5.0.0", "netcoreapp3.1", null, "5.1.2")] + [InlineData("5.0.0", "netcoreapp3.1", false, "5.2.0")] + [InlineData("5.0.0", "net6.0", true, "5.1.2")] + [InlineData("5.0.0", "net6.0", null, "5.1.2")] + [InlineData("5.0.0", "net6.0", false, "5.2.0")] + [InlineData("7.0.0", "net7.0", true, "7.0.1")] + [InlineData("7.0.0", "net7.0", null, "7.0.1")] + [InlineData("7.0.0", "net7.0", false, "7.1.2")] + [InlineData("7.0.0", "net8.0", true, "7.0.1")] + [InlineData("7.0.0", "net8.0", null, "7.0.1")] + [InlineData("7.0.0", "net8.0", false, "7.1.2")] + // MLL where main hive has a better match + [InlineData("6.0.0", "net6.0", true, "6.1.4")] // Global hive with better version (higher patch) + [InlineData("6.0.0", "net6.0", null, "6.1.4")] // MLL is on by default, so same as true + [InlineData("6.0.0", "net6.0", false, "6.1.3")] // No globla hive, so the main hive version is picked + public void FrameworkHiveSelection(string requestedVersion, string tfm, bool? multiLevelLookup, string resolvedVersion) { + // Multi-level lookup is only supported on Windows. + if (!OperatingSystem.IsWindows() && multiLevelLookup != false) + return; + RunTest( runtimeConfig => runtimeConfig - .WithFramework(MicrosoftNETCoreApp, "5.0.0")) - .ShouldHaveResolvedFramework(MicrosoftNETCoreApp, "5.1.2"); + .WithTfm(tfm) + .WithFramework(MicrosoftNETCoreApp, requestedVersion), + multiLevelLookup) + .ShouldHaveResolvedFramework(MicrosoftNETCoreApp, resolvedVersion); } [Fact] [PlatformSpecific(TestPlatforms.Windows)] // Multiple hives are only supported on Windows. - public void FrameworkHiveSelection_MainHiveWithBetterMatch() + public void FrameworkHiveSelection_CurrentDirectoryIsIgnored() { - RunTest( - runtimeConfig => runtimeConfig - .WithFramework(MicrosoftNETCoreApp, "6.0.0")) - .ShouldHaveResolvedFramework(MicrosoftNETCoreApp, "6.1.2"); + RunTest(new TestSettings() + .WithRuntimeConfigCustomizer(runtimeConfig => runtimeConfig + .WithTfm(Constants.Tfm.Net5) + .WithFramework(MicrosoftNETCoreApp, "5.0.0")) + .WithWorkingDirectory(SharedState.DotNetCurrentHive.BinPath), + multiLevelLookup: true) + .ShouldHaveResolvedFramework(MicrosoftNETCoreApp, "5.1.2"); } - [Fact] - [PlatformSpecific(TestPlatforms.Windows)] // Multiple hives are only supported on Windows. - public void FrameworkHiveSelection_CurrentDirectoryIsIgnored() + [Theory] + [InlineData("6.1.2", "net6.0", true, "6.1.2", false)] // No roll forward if --fx-version is used + [InlineData("6.1.2", "net6.0", null, "6.1.2", false)] + [InlineData("6.1.2", "net6.0", false, "6.1.2", false)] + [InlineData("6.1.2", "net7.0", true, "6.1.2", false)] + [InlineData("6.1.4", "net6.0", true, "6.1.4", true)] + [InlineData("6.1.4", "net6.0", null, "6.1.4", true)] + [InlineData("6.1.4", "net6.0", false, ResolvedFramework.NotFound, false)] + [InlineData("6.1.4", "net7.0", true, "6.1.4", true)] + [InlineData("7.1.2", "net6.0", true, "7.1.2", false)] // 7.1.2 is in both main and global hives - the main should always win with exact match + [InlineData("7.1.2", "net6.0", null, "7.1.2", false)] + [InlineData("7.1.2", "net6.0", false, "7.1.2", false)] + [InlineData("7.1.2", "net7.0", true, "7.1.2", false)] + public void FxVersionCLI(string fxVersion, string tfm, bool? multiLevelLookup, string resolvedVersion, bool fromGlobalHive) { + // Multi-level lookup is only supported on Windows. + if (!OperatingSystem.IsWindows() && multiLevelLookup != false) + return; + RunTest( - SharedState.DotNetMainHive, - SharedState.FrameworkReferenceApp, new TestSettings() - .WithRuntimeConfigCustomizer(runtimeConfig => runtimeConfig - .WithFramework(MicrosoftNETCoreApp, "5.0.0")) - .WithWorkingDirectory(SharedState.DotNetCurrentHive.BinPath)) - .ShouldHaveResolvedFramework(MicrosoftNETCoreApp, "5.2.0"); + .WithRuntimeConfigCustomizer( + runtimeConfig => runtimeConfig + .WithTfm(tfm) + .WithFramework(MicrosoftNETCoreApp, "4.0.0")) + .WithCommandLine(Constants.FxVersion.CommandLineArgument, fxVersion), + multiLevelLookup) + .ShouldHaveResolvedFrameworkOrFailToFind(MicrosoftNETCoreApp, resolvedVersion, fromGlobalHive ? SharedState.DotNetGlobalHive.BinPath : SharedState.DotNetMainHive.BinPath); } - private CommandResult RunTest(Func runtimeConfig) + private record struct FrameworkInfo(string Name, string Version, int Level, string Path); + + List GetExpectedFrameworks(bool? multiLevelLookup) { - using (TestOnlyProductBehavior.Enable(SharedState.DotNetMainHive.GreatestVersionHostFxrFilePath)) + // The runtimes should be ordered by version number + List expectedList = new(); + expectedList.Add(new FrameworkInfo(MicrosoftNETCoreApp, "5.2.0", 1, SharedState.DotNetMainHive.BinPath)); + expectedList.Add(new FrameworkInfo(MicrosoftNETCoreApp, "6.1.2", 1, SharedState.DotNetMainHive.BinPath)); + expectedList.Add(new FrameworkInfo(MicrosoftNETCoreApp, "6.1.3", 1, SharedState.DotNetMainHive.BinPath)); + expectedList.Add(new FrameworkInfo(MicrosoftNETCoreApp, "7.1.2", 1, SharedState.DotNetMainHive.BinPath)); + if (multiLevelLookup is null || multiLevelLookup == true) { - return RunTest( - SharedState.DotNetMainHive, - SharedState.FrameworkReferenceApp, - new TestSettings() - .WithRuntimeConfigCustomizer(runtimeConfig) - .WithEnvironment(Constants.TestOnlyEnvironmentVariables.GloballyRegisteredPath, SharedState.DotNetGlobalHive.BinPath) - .WithEnvironment( // Redirect the default install location to an invalid location so that a machine-wide install is not used - Constants.TestOnlyEnvironmentVariables.DefaultInstallPath, - System.IO.Path.Combine(SharedState.DotNetMainHive.BinPath, "invalid")), - // Must enable multi-level lookup otherwise multiple hives are not enabled - multiLevelLookup: true); + expectedList.Add(new FrameworkInfo(MicrosoftNETCoreApp, "5.1.2", 2, SharedState.DotNetGlobalHive.BinPath)); + expectedList.Add(new FrameworkInfo(MicrosoftNETCoreApp, "6.1.4", 2, SharedState.DotNetGlobalHive.BinPath)); + expectedList.Add(new FrameworkInfo(MicrosoftNETCoreApp, "6.2.0", 2, SharedState.DotNetGlobalHive.BinPath)); + expectedList.Add(new FrameworkInfo(MicrosoftNETCoreApp, "7.0.1", 2, SharedState.DotNetGlobalHive.BinPath)); + expectedList.Add(new FrameworkInfo(MicrosoftNETCoreApp, "7.1.2", 2, SharedState.DotNetGlobalHive.BinPath)); } + expectedList.Sort((a, b) => { + int result = a.Name.CompareTo(b.Name); + if (result != 0) + return result; + + result = a.Version.CompareTo(b.Version); + if (result != 0) + return result; + + return b.Level.CompareTo(a.Level); + }); + return expectedList; + } + + [Theory] + [InlineData(true)] + [InlineData(null)] + [InlineData(false)] + public void ListRuntimes(bool? multiLevelLookup) + { + // Multi-level lookup is only supported on Windows. + if (!OperatingSystem.IsWindows() && multiLevelLookup != false) + return; + + string expectedOutput = String.Join( + String.Empty, + GetExpectedFrameworks(multiLevelLookup) + .Select(t => $"{MicrosoftNETCoreApp} {t.Version} [{Path.Combine(t.Path, "shared", MicrosoftNETCoreApp)}]{Environment.NewLine}")); + + RunTest(new TestSettings() + .WithCommandLine("--list-runtimes"), + multiLevelLookup, + testApp: null) + .Should().HaveStdOut(expectedOutput); + } + + [Theory] + [InlineData(true)] + [InlineData(null)] + [InlineData(false)] + public void DotnetInfo(bool? multiLevelLookup) + { + // Multi-level lookup is only supported on Windows. + if (!OperatingSystem.IsWindows() && multiLevelLookup != false) + return; + + string expectedOutput = + $".NET runtimes installed:{Environment.NewLine}" + + String.Join(String.Empty, + GetExpectedFrameworks(multiLevelLookup) + .Select(t => $" {MicrosoftNETCoreApp} {t.Version} [{Path.Combine(t.Path, "shared", MicrosoftNETCoreApp)}]{Environment.NewLine}")); + + RunTest(new TestSettings() + .WithCommandLine("--info"), + multiLevelLookup, + testApp: null) + .Should().HaveStdOutContaining(expectedOutput); + } + + [Theory] + [InlineData("net5.0", true)] + [InlineData("net5.0", null)] + [InlineData("net5.0", false)] + [InlineData("net7.0", true)] + [InlineData("net7.0", null)] + [InlineData("net7.0", false)] + public void FrameworkResolutionError(string tfm, bool? multiLevelLookup) + { + // Multi-level lookup is only supported on Windows. + if (!OperatingSystem.IsWindows() && multiLevelLookup != false) + return; + + string expectedOutput = + $"The following frameworks were found:{Environment.NewLine}" + + String.Join(String.Empty, + GetExpectedFrameworks(multiLevelLookup) + .Select(t => $" {t.Version} at [{Path.Combine(t.Path, "shared", MicrosoftNETCoreApp)}]{Environment.NewLine}")); + + RunTest( + runtimeConfig => runtimeConfig + .WithTfm(tfm) + .WithFramework(MicrosoftNETCoreApp, "9999.9.9"), + multiLevelLookup) + .Should().Fail() + .And.HaveStdErrContaining(expectedOutput); + } + + private CommandResult RunTest(Func runtimeConfig, bool? multiLevelLookup = true) + => RunTest(new TestSettings().WithRuntimeConfigCustomizer(runtimeConfig), multiLevelLookup); + + private CommandResult RunTest(TestSettings testSettings, bool? multiLevelLookup) + => RunTest(testSettings, multiLevelLookup, SharedState.FrameworkReferenceApp); + + private CommandResult RunTest(TestSettings testSettings, bool? multiLevelLookup, TestApp testApp) + { + return RunTest( + SharedState.DotNetMainHive, + testApp, + testSettings + .WithEnvironment(Constants.TestOnlyEnvironmentVariables.GloballyRegisteredPath, SharedState.DotNetGlobalHive.BinPath) + .WithEnvironment( // Redirect the default install location to an invalid location so that a machine-wide install is not used + Constants.TestOnlyEnvironmentVariables.DefaultInstallPath, + System.IO.Path.Combine(SharedState.DotNetMainHive.BinPath, "invalid")), + // Must enable multi-level lookup otherwise multiple hives are not enabled + multiLevelLookup: multiLevelLookup); } public class SharedTestState : SharedTestStateBase @@ -82,23 +239,43 @@ public class SharedTestState : SharedTestStateBase public DotNetCli DotNetCurrentHive { get; } + private readonly IDisposable _testOnlyProductBehaviorScope; + public SharedTestState() { DotNetMainHive = DotNet("MainHive") .AddMicrosoftNETCoreAppFrameworkMockHostPolicy("5.2.0") .AddMicrosoftNETCoreAppFrameworkMockHostPolicy("6.1.2") + .AddMicrosoftNETCoreAppFrameworkMockHostPolicy("6.1.3") + .AddMicrosoftNETCoreAppFrameworkMockHostPolicy("7.1.2") .Build(); DotNetGlobalHive = DotNet("GlobalHive") .AddMicrosoftNETCoreAppFrameworkMockHostPolicy("5.1.2") + .AddMicrosoftNETCoreAppFrameworkMockHostPolicy("6.1.4") .AddMicrosoftNETCoreAppFrameworkMockHostPolicy("6.2.0") + .AddMicrosoftNETCoreAppFrameworkMockHostPolicy("7.0.1") + .AddMicrosoftNETCoreAppFrameworkMockHostPolicy("7.1.2") .Build(); DotNetCurrentHive = DotNet("CurrentHive") .AddMicrosoftNETCoreAppFrameworkMockHostPolicy("5.1.0") + .AddMicrosoftNETCoreAppFrameworkMockHostPolicy("7.3.0") .Build(); FrameworkReferenceApp = CreateFrameworkReferenceApp(); + + _testOnlyProductBehaviorScope = TestOnlyProductBehavior.Enable(DotNetMainHive.GreatestVersionHostFxrFilePath); + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + _testOnlyProductBehaviorScope.Dispose(); + } + + base.Dispose(disposing); } } } diff --git a/src/installer/tests/HostActivation.Tests/MultilevelSDKLookup.cs b/src/installer/tests/HostActivation.Tests/MultilevelSDKLookup.cs index 76d11337c03ab5..8ddb3dc4fea56d 100644 --- a/src/installer/tests/HostActivation.Tests/MultilevelSDKLookup.cs +++ b/src/installer/tests/HostActivation.Tests/MultilevelSDKLookup.cs @@ -2,9 +2,11 @@ // The .NET Foundation licenses this file to you under the MIT license. using Microsoft.DotNet.Cli.Build; +using Microsoft.DotNet.Cli.Build.Framework; using System; using System.Collections.Generic; using System.IO; +using System.Linq; using System.Runtime.InteropServices; using Xunit; @@ -12,14 +14,6 @@ namespace Microsoft.DotNet.CoreSetup.Test.HostActivation { public class MultilevelSDKLookup : IDisposable { - private static readonly IDictionary s_DefaultEnvironment = new Dictionary() - { - {"COREHOST_TRACE", "1" }, - // The SDK being used may be crossgen'd for a different architecture than we are building for. - // Turn off ready to run, so an x64 crossgen'd SDK can be loaded in an x86 process. - {"COMPlus_ReadyToRun", "0" }, - }; - private readonly RepoDirectoriesProvider RepoDirectories; private readonly DotNetCli DotNet; @@ -101,15 +95,7 @@ public void SdkMultilevelLookup_Global_Json_Single_Digit_Patch_Rollup() // Exe: empty // Reg: empty // Expected: no compatible version and a specific error messages - DotNet.Exec("help") - .WorkingDirectory(_currentWorkingDir) - .WithUserProfile(_userDir) - .Environment(s_DefaultEnvironment) - .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "1") - .EnvironmentVariable(Constants.TestOnlyEnvironmentVariables.GloballyRegisteredPath, _regDir) - .CaptureStdOut() - .CaptureStdErr() - .Execute(fExpectedToFail: true) + RunTest() .Should().Fail() .And.HaveStdErrContaining("A compatible installed .NET SDK for global.json version"); @@ -122,15 +108,7 @@ public void SdkMultilevelLookup_Global_Json_Single_Digit_Patch_Rollup() // Exe: 9999.4.1, 9999.3.4-dummy // Reg: empty // Expected: no compatible version and a specific error message - DotNet.Exec("help") - .WorkingDirectory(_currentWorkingDir) - .WithUserProfile(_userDir) - .Environment(s_DefaultEnvironment) - .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "1") - .EnvironmentVariable(Constants.TestOnlyEnvironmentVariables.GloballyRegisteredPath, _regDir) - .CaptureStdOut() - .CaptureStdErr() - .Execute(fExpectedToFail: true) + RunTest() .Should().Fail() .And.HaveStdErrContaining("A compatible installed .NET SDK for global.json version"); @@ -143,15 +121,7 @@ public void SdkMultilevelLookup_Global_Json_Single_Digit_Patch_Rollup() // Exe: 9999.4.1, 9999.3.4-dummy // Reg: 9999.3.3 // Expected: no compatible version and a specific error message - DotNet.Exec("help") - .WorkingDirectory(_currentWorkingDir) - .WithUserProfile(_userDir) - .Environment(s_DefaultEnvironment) - .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "1") - .EnvironmentVariable(Constants.TestOnlyEnvironmentVariables.GloballyRegisteredPath, _regDir) - .CaptureStdOut() - .CaptureStdErr() - .Execute(fExpectedToFail: true) + RunTest() .Should().Fail() .And.HaveStdErrContaining("A compatible installed .NET SDK for global.json version"); @@ -164,15 +134,7 @@ public void SdkMultilevelLookup_Global_Json_Single_Digit_Patch_Rollup() // Exe: 9999.4.1, 9999.3.4-dummy, 9999.3.4 // Reg: 9999.3.3 // Expected: 9999.3.4 from exe dir - DotNet.Exec("help") - .WorkingDirectory(_currentWorkingDir) - .WithUserProfile(_userDir) - .Environment(s_DefaultEnvironment) - .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "1") - .EnvironmentVariable(Constants.TestOnlyEnvironmentVariables.GloballyRegisteredPath, _regDir) - .CaptureStdOut() - .CaptureStdErr() - .Execute() + RunTest() .Should().Pass() .And.HaveStdErrContaining(Path.Combine(_exeSelectedMessage, "9999.3.4", _dotnetSdkDllMessageTerminator)); @@ -185,15 +147,7 @@ public void SdkMultilevelLookup_Global_Json_Single_Digit_Patch_Rollup() // Exe: 9999.4.1, 9999.3.4-dummy, 9999.3.4 // Reg: 9999.3.3, 9999.3.5-dummy // Expected: 9999.3.5-dummy from reg dir - DotNet.Exec("help") - .WorkingDirectory(_currentWorkingDir) - .WithUserProfile(_userDir) - .Environment(s_DefaultEnvironment) - .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "1") - .EnvironmentVariable(Constants.TestOnlyEnvironmentVariables.GloballyRegisteredPath, _regDir) - .CaptureStdOut() - .CaptureStdErr() - .Execute() + RunTest() .Should().Pass() .And.HaveStdErrContaining(Path.Combine(_regSelectedMessage, "9999.3.5-dummy", _dotnetSdkDllMessageTerminator)); @@ -206,15 +160,7 @@ public void SdkMultilevelLookup_Global_Json_Single_Digit_Patch_Rollup() // Exe: 9999.4.1, 9999.3.4-dummy, 9999.3.4, 9999.3.600 // Reg: 9999.3.3, 9999.3.5-dummy // Expected: 9999.3.5-dummy from reg dir - DotNet.Exec("help") - .WorkingDirectory(_currentWorkingDir) - .WithUserProfile(_userDir) - .Environment(s_DefaultEnvironment) - .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "1") - .EnvironmentVariable(Constants.TestOnlyEnvironmentVariables.GloballyRegisteredPath, _regDir) - .CaptureStdOut() - .CaptureStdErr() - .Execute() + RunTest() .Should().Pass() .And.HaveStdErrContaining(Path.Combine(_regSelectedMessage, "9999.3.5-dummy", _dotnetSdkDllMessageTerminator)); @@ -227,27 +173,12 @@ public void SdkMultilevelLookup_Global_Json_Single_Digit_Patch_Rollup() // Exe: 9999.4.1, 9999.3.4-dummy, 9999.3.4, 9999.3.600, 9999.3.4-global-dummy // Reg: 9999.3.3, 9999.3.5-dummy // Expected: 9999.3.4-global-dummy from exe dir - DotNet.Exec("help") - .WorkingDirectory(_currentWorkingDir) - .WithUserProfile(_userDir) - .Environment(s_DefaultEnvironment) - .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "1") - .EnvironmentVariable(Constants.TestOnlyEnvironmentVariables.GloballyRegisteredPath, _regDir) - .CaptureStdOut() - .CaptureStdErr() - .Execute() + RunTest() .Should().Pass() .And.HaveStdErrContaining(Path.Combine(_exeSelectedMessage, "9999.3.4-global-dummy", _dotnetSdkDllMessageTerminator)); // Verify we have the expected SDK versions - DotNet.Exec("--list-sdks") - .WorkingDirectory(_currentWorkingDir) - .WithUserProfile(_userDir) - .Environment(s_DefaultEnvironment) - .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "1") - .EnvironmentVariable(Constants.TestOnlyEnvironmentVariables.GloballyRegisteredPath, _regDir) - .CaptureStdOut() - .Execute() + RunTest("--list-sdks") .Should().Pass() .And.HaveStdOutContaining("9999.3.4-dummy") .And.HaveStdOutContaining("9999.3.4-global-dummy") @@ -271,15 +202,7 @@ public void SdkMultilevelLookup_Global_Json_Two_Part_Patch_Rollup() // Exe: empty // Reg: empty // Expected: no compatible version and a specific error messages - DotNet.Exec("help") - .WorkingDirectory(_currentWorkingDir) - .WithUserProfile(_userDir) - .Environment(s_DefaultEnvironment) - .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "1") - .EnvironmentVariable(Constants.TestOnlyEnvironmentVariables.GloballyRegisteredPath, _regDir) - .CaptureStdOut() - .CaptureStdErr() - .Execute(fExpectedToFail: true) + RunTest() .Should().Fail() .And.HaveStdErrContaining("A compatible installed .NET SDK for global.json version"); @@ -292,15 +215,7 @@ public void SdkMultilevelLookup_Global_Json_Two_Part_Patch_Rollup() // Exe: empty // Reg: 9999.3.57, 9999.3.4-dummy // Expected: no compatible version and a specific error message - DotNet.Exec("help") - .WorkingDirectory(_currentWorkingDir) - .WithUserProfile(_userDir) - .Environment(s_DefaultEnvironment) - .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "1") - .EnvironmentVariable(Constants.TestOnlyEnvironmentVariables.GloballyRegisteredPath, _regDir) - .CaptureStdOut() - .CaptureStdErr() - .Execute(fExpectedToFail: true) + RunTest() .Should().Fail() .And.HaveStdErrContaining("A compatible installed .NET SDK for global.json version"); @@ -313,15 +228,7 @@ public void SdkMultilevelLookup_Global_Json_Two_Part_Patch_Rollup() // Exe: 9999.3.300, 9999.7.304-global-dummy // Reg: 9999.3.57, 9999.3.4-dummy // Expected: no compatible version and a specific error message - DotNet.Exec("help") - .WorkingDirectory(_currentWorkingDir) - .WithUserProfile(_userDir) - .Environment(s_DefaultEnvironment) - .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "1") - .EnvironmentVariable(Constants.TestOnlyEnvironmentVariables.GloballyRegisteredPath, _regDir) - .CaptureStdOut() - .CaptureStdErr() - .Execute(fExpectedToFail: true) + RunTest() .Should().Fail() .And.HaveStdErrContaining("A compatible installed .NET SDK for global.json version"); @@ -334,15 +241,7 @@ public void SdkMultilevelLookup_Global_Json_Two_Part_Patch_Rollup() // Exe: 9999.3.300, 9999.7.304-global-dummy // Reg: 9999.3.57, 9999.3.4-dummy, 9999.3.304 // Expected: 9999.3.304 from reg dir - DotNet.Exec("help") - .WorkingDirectory(_currentWorkingDir) - .WithUserProfile(_userDir) - .Environment(s_DefaultEnvironment) - .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "1") - .EnvironmentVariable(Constants.TestOnlyEnvironmentVariables.GloballyRegisteredPath, _regDir) - .CaptureStdOut() - .CaptureStdErr() - .Execute() + RunTest() .Should().Pass() .And.HaveStdErrContaining(Path.Combine(_regSelectedMessage, "9999.3.304", _dotnetSdkDllMessageTerminator)); @@ -355,15 +254,7 @@ public void SdkMultilevelLookup_Global_Json_Two_Part_Patch_Rollup() // Exe: 9999.3.300, 9999.7.304-global-dummy, 9999.3.399, 9999.3.399-dummy, 9999.3.400 // Reg: 9999.3.57, 9999.3.4-dummy, 9999.3.304 // Expected: 9999.3.399 from exe dir - DotNet.Exec("help") - .WorkingDirectory(_currentWorkingDir) - .WithUserProfile(_userDir) - .Environment(s_DefaultEnvironment) - .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "1") - .EnvironmentVariable(Constants.TestOnlyEnvironmentVariables.GloballyRegisteredPath, _regDir) - .CaptureStdOut() - .CaptureStdErr() - .Execute() + RunTest() .Should().Pass() .And.HaveStdErrContaining(Path.Combine(_exeSelectedMessage, "9999.3.399", _dotnetSdkDllMessageTerminator)); @@ -377,15 +268,7 @@ public void SdkMultilevelLookup_Global_Json_Two_Part_Patch_Rollup() // Exe: 9999.3.300, 9999.7.304-global-dummy, 9999.3.399, 9999.3.399-dummy, 9999.3.400, 9999.3.2400, 9999.3.3004 // Reg: 9999.3.57, 9999.3.4-dummy, 9999.3.304, 9999.3.2400, 9999.3.3004 // Expected: 9999.3.399 from exe dir - DotNet.Exec("help") - .WorkingDirectory(_currentWorkingDir) - .WithUserProfile(_userDir) - .Environment(s_DefaultEnvironment) - .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "1") - .EnvironmentVariable(Constants.TestOnlyEnvironmentVariables.GloballyRegisteredPath, _regDir) - .CaptureStdOut() - .CaptureStdErr() - .Execute() + RunTest() .Should().Pass() .And.HaveStdErrContaining(Path.Combine(_exeSelectedMessage, "9999.3.399", _dotnetSdkDllMessageTerminator)); @@ -398,27 +281,12 @@ public void SdkMultilevelLookup_Global_Json_Two_Part_Patch_Rollup() // Exe: 9999.3.300, 9999.7.304-global-dummy, 9999.3.399, 9999.3.399-dummy, 9999.3.400, 9999.3.2400, 9999.3.3004 // Reg: 9999.3.57, 9999.3.4-dummy, 9999.3.304, 9999.3.2400, 9999.3.3004, 9999.3.304-global-dummy // Expected: 9999.3.304-global-dummy from reg dir - DotNet.Exec("help") - .WorkingDirectory(_currentWorkingDir) - .WithUserProfile(_userDir) - .Environment(s_DefaultEnvironment) - .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "1") - .EnvironmentVariable(Constants.TestOnlyEnvironmentVariables.GloballyRegisteredPath, _regDir) - .CaptureStdOut() - .CaptureStdErr() - .Execute() + RunTest() .Should().Pass() .And.HaveStdErrContaining(Path.Combine(_regSelectedMessage, "9999.3.304-global-dummy", _dotnetSdkDllMessageTerminator)); // Verify we have the expected SDK versions - DotNet.Exec("--list-sdks") - .WorkingDirectory(_currentWorkingDir) - .WithUserProfile(_userDir) - .Environment(s_DefaultEnvironment) - .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "1") - .EnvironmentVariable(Constants.TestOnlyEnvironmentVariables.GloballyRegisteredPath, _regDir) - .CaptureStdOut() - .Execute() + RunTest("--list-sdks") .Should().Pass() .And.HaveStdOutContaining("9999.3.57") .And.HaveStdOutContaining("9999.3.4-dummy") @@ -448,15 +316,7 @@ public void SdkMultilevelLookup_Precedential_Order() // Exe: empty // Reg: 9999.0.4 // Expected: 9999.0.4 from reg dir - DotNet.Exec("help") - .WorkingDirectory(_currentWorkingDir) - .WithUserProfile(_userDir) - .Environment(s_DefaultEnvironment) - .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "1") - .EnvironmentVariable(Constants.TestOnlyEnvironmentVariables.GloballyRegisteredPath, _regDir) - .CaptureStdOut() - .CaptureStdErr() - .Execute() + RunTest() .Should().Pass() .And.HaveStdErrContaining(Path.Combine(_regSelectedMessage, "9999.0.4", _dotnetSdkDllMessageTerminator)); @@ -469,15 +329,7 @@ public void SdkMultilevelLookup_Precedential_Order() // Exe: 9999.0.4 // Reg: 9999.0.4 // Expected: 9999.0.4 from exe dir - DotNet.Exec("help") - .WorkingDirectory(_currentWorkingDir) - .WithUserProfile(_userDir) - .Environment(s_DefaultEnvironment) - .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "1") - .EnvironmentVariable(Constants.TestOnlyEnvironmentVariables.GloballyRegisteredPath, _regDir) - .CaptureStdOut() - .CaptureStdErr() - .Execute() + RunTest() .Should().Pass() .And.HaveStdErrContaining(Path.Combine(_exeSelectedMessage, "9999.0.4", _dotnetSdkDllMessageTerminator)); } @@ -515,11 +367,9 @@ public void SdkMultilevelLookup_RegistryAccess() DotNet.Exec("help") .WorkingDirectory(_currentWorkingDir) .WithUserProfile(_userDir) - .Environment(s_DefaultEnvironment) - .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "1") + .MultilevelLookup(true) .ApplyRegisteredInstallLocationOverride(registeredInstallLocationOverride) - .CaptureStdOut() - .CaptureStdErr() + .EnableTracingAndCaptureOutputs() .Execute() .Should().Pass() .And.HaveStdErrContaining(Path.Combine(_regSelectedMessage, "9999.0.4", _dotnetSdkDllMessageTerminator)); @@ -541,15 +391,7 @@ public void SdkMultilevelLookup_Must_Pick_The_Highest_Semantic_Version() // Exe: empty // Reg: 9999.0.0, 9999.0.3-dummy // Expected: 9999.0.3-dummy from reg dir - DotNet.Exec("help") - .WorkingDirectory(_currentWorkingDir) - .WithUserProfile(_userDir) - .Environment(s_DefaultEnvironment) - .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "1") - .EnvironmentVariable(Constants.TestOnlyEnvironmentVariables.GloballyRegisteredPath, _regDir) - .CaptureStdOut() - .CaptureStdErr() - .Execute() + RunTest() .Should().Pass() .And.HaveStdErrContaining(Path.Combine(_regSelectedMessage, "9999.0.3-dummy", _dotnetSdkDllMessageTerminator)); @@ -562,15 +404,7 @@ public void SdkMultilevelLookup_Must_Pick_The_Highest_Semantic_Version() // Exe: 9999.0.3 // Reg: 9999.0.0, 9999.0.3-dummy // Expected: 9999.0.3 from exe dir - DotNet.Exec("help") - .WorkingDirectory(_currentWorkingDir) - .WithUserProfile(_userDir) - .Environment(s_DefaultEnvironment) - .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "1") - .EnvironmentVariable(Constants.TestOnlyEnvironmentVariables.GloballyRegisteredPath, _regDir) - .CaptureStdOut() - .CaptureStdErr() - .Execute() + RunTest() .Should().Pass() .And.HaveStdErrContaining(Path.Combine(_exeSelectedMessage, "9999.0.3", _dotnetSdkDllMessageTerminator)); @@ -585,15 +419,7 @@ public void SdkMultilevelLookup_Must_Pick_The_Highest_Semantic_Version() // Exe: 9999.0.3 // Reg: 9999.0.0, 9999.0.3-dummy, 9999.0.100 // Expected: 9999.0.100 from reg dir - DotNet.Exec("help") - .WorkingDirectory(_currentWorkingDir) - .WithUserProfile(_userDir) - .Environment(s_DefaultEnvironment) - .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "1") - .EnvironmentVariable(Constants.TestOnlyEnvironmentVariables.GloballyRegisteredPath, _regDir) - .CaptureStdOut() - .CaptureStdErr() - .Execute() + RunTest() .Should().Pass() .And.HaveStdErrContaining(Path.Combine(_regSelectedMessage, "9999.0.100", _dotnetSdkDllMessageTerminator)); @@ -606,15 +432,7 @@ public void SdkMultilevelLookup_Must_Pick_The_Highest_Semantic_Version() // Exe: 9999.0.3, 9999.0.80 // Reg: 9999.0.0, 9999.0.3-dummy, 9999.0.100 // Expected: 9999.0.100 from reg dir - DotNet.Exec("help") - .WorkingDirectory(_currentWorkingDir) - .WithUserProfile(_userDir) - .Environment(s_DefaultEnvironment) - .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "1") - .EnvironmentVariable(Constants.TestOnlyEnvironmentVariables.GloballyRegisteredPath, _regDir) - .CaptureStdOut() - .CaptureStdErr() - .Execute() + RunTest() .Should().Pass() .And.HaveStdErrContaining(Path.Combine(_regSelectedMessage, "9999.0.100", _dotnetSdkDllMessageTerminator)); @@ -627,15 +445,7 @@ public void SdkMultilevelLookup_Must_Pick_The_Highest_Semantic_Version() // Exe: 9999.0.3, 9999.0.80, 9999.0.5500000 // Reg: 9999.0.0, 9999.0.3-dummy, 9999.0.100 // Expected: 9999.0.5500000 from exe dir - DotNet.Exec("help") - .WorkingDirectory(_currentWorkingDir) - .WithUserProfile(_userDir) - .Environment(s_DefaultEnvironment) - .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "1") - .EnvironmentVariable(Constants.TestOnlyEnvironmentVariables.GloballyRegisteredPath, _regDir) - .CaptureStdOut() - .CaptureStdErr() - .Execute() + RunTest() .Should().Pass() .And.HaveStdErrContaining(Path.Combine(_exeSelectedMessage, "9999.0.5500000", _dotnetSdkDllMessageTerminator)); @@ -648,27 +458,12 @@ public void SdkMultilevelLookup_Must_Pick_The_Highest_Semantic_Version() // Exe: 9999.0.3, 9999.0.80, 9999.0.5500000 // Reg: 9999.0.0, 9999.0.3-dummy, 9999.0.100, 9999.0.52000000 // Expected: 9999.0.52000000 from reg dir - DotNet.Exec("help") - .WorkingDirectory(_currentWorkingDir) - .WithUserProfile(_userDir) - .Environment(s_DefaultEnvironment) - .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "1") - .EnvironmentVariable(Constants.TestOnlyEnvironmentVariables.GloballyRegisteredPath, _regDir) - .CaptureStdOut() - .CaptureStdErr() - .Execute() + RunTest() .Should().Pass() .And.HaveStdErrContaining(Path.Combine(_regSelectedMessage, "9999.0.52000000", _dotnetSdkDllMessageTerminator)); // Verify we have the expected SDK versions - DotNet.Exec("--list-sdks") - .WorkingDirectory(_currentWorkingDir) - .WithUserProfile(_userDir) - .Environment(s_DefaultEnvironment) - .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "1") - .EnvironmentVariable(Constants.TestOnlyEnvironmentVariables.GloballyRegisteredPath, _regDir) - .CaptureStdOut() - .Execute() + RunTest("--list-sdks") .Should().Pass() .And.HaveStdOutContaining("9999.0.0") .And.HaveStdOutContaining("9999.0.3-dummy") @@ -679,6 +474,104 @@ public void SdkMultilevelLookup_Must_Pick_The_Highest_Semantic_Version() .And.HaveStdOutContaining("9999.0.52000000"); } + List<(string version, string rootPath)> AddSdkVersionsAndGetExpectedList(bool? multiLevelLookup) + { + AddAvailableSdkVersions(_exeSdkBaseDir, "5.0.2"); + AddAvailableSdkVersions(_exeSdkBaseDir, "6.1.1"); + AddAvailableSdkVersions(_exeSdkBaseDir, "7.1.2"); + AddAvailableSdkVersions(_regSdkBaseDir, "6.2.0"); + AddAvailableSdkVersions(_regSdkBaseDir, "7.0.1"); + + // The SDKs should be ordered by version number + List<(string version, string rootPath)> expectedList = new(); + expectedList.Add(("5.0.2", _exeSdkBaseDir)); + expectedList.Add(("6.1.1", _exeSdkBaseDir)); + expectedList.Add(("7.1.2", _exeSdkBaseDir)); + if (multiLevelLookup is null || multiLevelLookup == true) + { + expectedList.Add(("6.2.0", _regSdkBaseDir)); + expectedList.Add(("7.0.1", _regSdkBaseDir)); + } + expectedList.Sort((a, b) => a.version.CompareTo(b.version)); + return expectedList; + } + + [Theory] + [InlineData(true)] + [InlineData(null)] + [InlineData(false)] + public void ListSdks(bool? multiLevelLookup) + { + // Multi-level lookup is only supported on Windows. + if (!OperatingSystem.IsWindows() && multiLevelLookup != false) + return; + + var expectedList = AddSdkVersionsAndGetExpectedList(multiLevelLookup); + string expectedOutput = String.Join(String.Empty, expectedList.Select(t => $"{t.version} [{t.rootPath}]{Environment.NewLine}")); + + RunTest("--list-sdks", multiLevelLookup) + .Should().Pass() + .And.HaveStdOut(expectedOutput); + } + + [Theory] + [InlineData(true)] + [InlineData(null)] + [InlineData(false)] + public void SdkResolutionError(bool? multiLevelLookup) + { + // Multi-level lookup is only supported on Windows. + if (!OperatingSystem.IsWindows() && multiLevelLookup != false) + return; + + // Set specified SDK version = 9999.3.4-global-dummy - such SDK doesn't exist + SetGlobalJsonVersion("SingleDigit-global.json"); + + // When we fail to resolve SDK version, we print out all available SDKs + var expectedList = AddSdkVersionsAndGetExpectedList(multiLevelLookup); + string expectedOutput = String.Join(String.Empty, expectedList.Select(t => $" {t.version} [{t.rootPath}]{Environment.NewLine}")); + + RunTest("help", multiLevelLookup) + .Should().Fail() + .And.HaveStdOutContaining(expectedOutput); + } + + [Theory] + [InlineData(true)] + [InlineData(null)] + [InlineData(false)] + public void DotnetInfo(bool? multiLevelLookup) + { + // Multi-level lookup is only supported on Windows. + if (!OperatingSystem.IsWindows() && multiLevelLookup != false) + return; + + var expectedList = AddSdkVersionsAndGetExpectedList(multiLevelLookup); + string expectedOutput = + $".NET SDKs installed:{Environment.NewLine}" + + String.Join(String.Empty, expectedList.Select(t => $" {t.version} [{t.rootPath}]{Environment.NewLine}")); + + RunTest("--info", multiLevelLookup) + .Should().Pass() + .And.HaveStdOutContaining(expectedOutput); + } + + CommandResult RunTest() => RunTest("help"); + + CommandResult RunTest(string command, bool? multiLevelLookup = true) + { + return DotNet.Exec(command) + .WorkingDirectory(_currentWorkingDir) + .WithUserProfile(_userDir) + .MultilevelLookup(multiLevelLookup) + .EnvironmentVariable(Constants.TestOnlyEnvironmentVariables.GloballyRegisteredPath, _regDir) + .EnvironmentVariable( // Redirect the default install location to an invalid location so that a machine-wide install is not used + Constants.TestOnlyEnvironmentVariables.DefaultInstallPath, + System.IO.Path.Combine(_userDir, "invalid")) + .EnableTracingAndCaptureOutputs() + .Execute(); + } + // This method adds a list of new sdk version folders in the specified directory. // The actual contents are 'fake' and the mininum required for SDK discovery. // The dotnet.runtimeconfig.json created uses a dummy framework version (9999.0.0) diff --git a/src/installer/tests/HostActivation.Tests/MultilevelSharedFxLookup.DepsVersion.cs b/src/installer/tests/HostActivation.Tests/MultilevelSharedFxLookup.DepsVersion.cs deleted file mode 100644 index 65b5ff2372308d..00000000000000 --- a/src/installer/tests/HostActivation.Tests/MultilevelSharedFxLookup.DepsVersion.cs +++ /dev/null @@ -1,130 +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 Newtonsoft.Json.Linq; -using System.IO; -using Xunit; - -namespace Microsoft.DotNet.CoreSetup.Test.HostActivation -{ - public partial class MultilevelSharedFxLookup - { - [Fact] - public void TPA_Version_Check_App_Wins() - { - string appAssembly; - string uberAssembly; - string netcoreAssembly; - - // Apps wins, 9999.0.0.1 vs Uber (existing version) and NetCore (also existing version) - var fixture = ConfigureAppAndFrameworks("99.0.0.1", null, "7777.0.0", out appAssembly, out uberAssembly, out netcoreAssembly); - var dotnet = fixture.BuiltDotnet; - var appDll = fixture.TestProject.AppDll; - - dotnet.Exec(appDll) - .WorkingDirectory(_currentWorkingDir) - .EnvironmentVariable("COREHOST_TRACE", "1") - .CaptureStdOut() - .CaptureStdErr() - .Execute() - .Should().Pass() - // Verify final selection in TRUSTED_PLATFORM_ASSEMBLIES - .And.HaveStdErrContaining($"{appAssembly}{Path.PathSeparator}") - .And.NotHaveStdErrContaining($"{netcoreAssembly}{Path.PathSeparator}") - .And.NotHaveStdErrContaining($"{uberAssembly}{Path.PathSeparator}"); - } - - [Theory] - [InlineData("0.0.0.1", "", "7777.0.0")] // Uber wins, existing assembly version vs app (0.0.0.1) and NetCore (also existing version) - [InlineData("99.0.0.1", "99.0.0.1", "7777.0.0")] // Tie case, no roll forward - [InlineData("99.0.0.1", "99.0.0.1", "7777.0.1")] // Tie case, patch roll forward - [InlineData("99.0.0.1", "99.0.0.1", "7777.1.0")] // Tie case, minor roll forward - [InlineData("99.0.0.1", "99.0.0.1", "7778.0.0")] // Tie case, major roll forward - public void TPA_Version_Check_UberFx_Wins(string appAssemblyVersion, string uberFxAssemblyVersion, string uberProductVersion) - { - string appAssembly; - string uberAssembly; - string netcoreAssembly; - - var fixture = ConfigureAppAndFrameworks(appAssemblyVersion, uberFxAssemblyVersion, uberProductVersion, out appAssembly, out uberAssembly, out netcoreAssembly); - var dotnet = fixture.BuiltDotnet; - var appDll = fixture.TestProject.AppDll; - - dotnet.Exec(appDll) - .WorkingDirectory(_currentWorkingDir) - .EnvironmentVariable("COREHOST_TRACE", "1") - .EnvironmentVariable("DOTNET_ROLL_FORWARD_ON_NO_CANDIDATE_FX", "2") // Allow major roll forward - .CaptureStdOut() - .CaptureStdErr() - .Execute() - .Should().Pass() - .And.HaveStdErrContaining(Path.Combine(_exeFoundUberFxMessage, uberProductVersion)) - // Verify final selection in TRUSTED_PLATFORM_ASSEMBLIES - .And.HaveStdErrContaining($"{uberAssembly}{Path.PathSeparator}") - .And.NotHaveStdErrContaining($"{netcoreAssembly}{Path.PathSeparator}") - .And.NotHaveStdErrContaining($"{appAssembly}{Path.PathSeparator}"); - } - - [Fact] - public void TPA_Version_Check_NetCore_Wins() - { - string appAssembly; - string uberAssembly; - string netcoreAssembly; - - // NetCore wins, existing assembly version vs app (0.0.0.1) and Uber (0.0.0.2) - var fixture = ConfigureAppAndFrameworks("0.0.0.1", "0.0.0.2", "7777.0.0", out appAssembly, out uberAssembly, out netcoreAssembly); - var dotnet = fixture.BuiltDotnet; - var appDll = fixture.TestProject.AppDll; - - dotnet.Exec(appDll) - .WorkingDirectory(_currentWorkingDir) - .EnvironmentVariable("COREHOST_TRACE", "1") - .CaptureStdOut() - .CaptureStdErr() - .Execute() - .Should().Pass() - // Verify final selection in TRUSTED_PLATFORM_ASSEMBLIES - .And.HaveStdErrContaining($"{netcoreAssembly}{Path.PathSeparator}") - .And.NotHaveStdErrContaining($"{appAssembly}{Path.PathSeparator}") - .And.NotHaveStdErrContaining($"{uberAssembly}{Path.PathSeparator}"); - } - - private TestProjectFixture ConfigureAppAndFrameworks(string appAssemblyVersion, string uberFxAssemblyVersion, string uberFxProductVersion, out string appAssembly, out string uberAssembly, out string netcoreAssembly) - { - const string fileVersion = "0.0.0.9"; - var fixture = SharedFxLookupPortableAppFixture - .Copy(); - - if (!string.IsNullOrEmpty(uberFxAssemblyVersion)) - { - // Modify Uber Fx's deps.json - SharedFramework.CreateUberFrameworkArtifacts(_builtSharedFxDir, _builtSharedUberFxDir, uberFxAssemblyVersion, fileVersion); - } - - // Set desired version = 7777.0.0 - string runtimeConfig = Path.Combine(fixture.TestProject.OutputDirectory, "SharedFxLookupPortableApp.runtimeconfig.json"); - SharedFramework.SetRuntimeConfigJson(runtimeConfig, "7777.0.0", null, useUberFramework: true); - - // Add versions in the exe folder - SharedFramework.AddAvailableSharedFxVersions(_builtSharedFxDir, _exeSharedFxBaseDir, "9999.0.0"); - SharedFramework.AddAvailableSharedUberFxVersions(_builtSharedUberFxDir, _exeSharedUberFxBaseDir, "9999.0.0", uberFxProductVersion); - - // Copy NetCoreApp's copy of the assembly to the app location - netcoreAssembly = Path.Combine(_exeSharedFxBaseDir, "9999.0.0", "System.Collections.Immutable.dll"); - appAssembly = Path.Combine(fixture.TestProject.OutputDirectory, "System.Collections.Immutable.dll"); - File.Copy(netcoreAssembly, appAssembly); - - // Modify the app's deps.json to add System.Collections.Immmutable - string appDepsJson = Path.Combine(fixture.TestProject.OutputDirectory, "SharedFxLookupPortableApp.deps.json"); - JObject versionInfo = new JObject(); - versionInfo.Add(new JProperty("assemblyVersion", appAssemblyVersion)); - versionInfo.Add(new JProperty("fileVersion", fileVersion)); - SharedFramework.AddReferenceToDepsJson(appDepsJson, "SharedFxLookupPortableApp/1.0.0", "System.Collections.Immutable", "1.0.0", versionInfo); - - uberAssembly = Path.Combine(_exeSharedUberFxBaseDir, uberFxProductVersion, "System.Collections.Immutable.dll"); - - return fixture; - } - } -} diff --git a/src/installer/tests/HostActivation.Tests/MultilevelSharedFxLookup.cs b/src/installer/tests/HostActivation.Tests/MultilevelSharedFxLookup.cs deleted file mode 100644 index 788963fa33e49b..00000000000000 --- a/src/installer/tests/HostActivation.Tests/MultilevelSharedFxLookup.cs +++ /dev/null @@ -1,347 +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.Runtime.InteropServices; -using Xunit; - -namespace Microsoft.DotNet.CoreSetup.Test.HostActivation -{ - public partial class MultilevelSharedFxLookup : IDisposable - { - private const string SystemCollectionsImmutableFileVersion = "88.2.3.4"; - private const string SystemCollectionsImmutableAssemblyVersion = "88.0.1.2"; - - private readonly RepoDirectoriesProvider RepoDirectories; - private readonly TestProjectFixture SharedFxLookupPortableAppFixture; - - private readonly string _currentWorkingDir; - private readonly string _userDir; - private readonly string _exeDir; - private readonly string _regDir; - private readonly string _cwdSharedFxBaseDir; - private readonly string _cwdSharedUberFxBaseDir; - private readonly string _userSharedFxBaseDir; - private readonly string _userSharedUberFxBaseDir; - private readonly string _exeSharedFxBaseDir; - private readonly string _exeSharedUberFxBaseDir; - private readonly string _regSharedFxBaseDir; - private readonly string _regSharedUberFxBaseDir; - private readonly string _builtSharedFxDir; - private readonly string _builtSharedUberFxDir; - - private readonly string _exeSelectedMessage; - private readonly string _regSelectedMessage; - - private readonly string _exeFoundUberFxMessage; - - private readonly string _sharedFxVersion; - private readonly string _multilevelDir; - private readonly string _builtDotnet; - private readonly string _hostPolicyDllName; - - private readonly IDisposable _testOnlyProductBehaviorMarker; - - public MultilevelSharedFxLookup() - { - // 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"); - _builtDotnet = Path.Combine(artifactsDir, "sharedFrameworkPublish"); - - // The dotnetMultilevelSharedFxLookup dir will contain some folders and files that will be - // necessary to perform the tests - string baseMultilevelDir = Path.Combine(artifactsDir, "dotnetMultilevelSharedFxLookup"); - _multilevelDir = SharedFramework.CalculateUniqueTestDirectory(baseMultilevelDir); - - // The three tested locations will be the cwd, the user folder and the exe dir. Both cwd and exe dir - // are easily overwritten, so they will be placed inside the multilevel folder. The actual user location will - // be used during tests - _currentWorkingDir = Path.Combine(_multilevelDir, "cwd"); - _userDir = Path.Combine(_multilevelDir, "user"); - _exeDir = Path.Combine(_multilevelDir, "exe"); - _regDir = Path.Combine(_multilevelDir, "reg"); - - RepoDirectories = new RepoDirectoriesProvider(builtDotnet: _exeDir); - - // SharedFxBaseDirs contain all available version folders - _cwdSharedFxBaseDir = Path.Combine(_currentWorkingDir, "shared", "Microsoft.NETCore.App"); - _userSharedFxBaseDir = Path.Combine(_userDir, ".dotnet", RepoDirectories.BuildArchitecture, "shared", "Microsoft.NETCore.App"); - _exeSharedFxBaseDir = Path.Combine(_exeDir, "shared", "Microsoft.NETCore.App"); - _regSharedFxBaseDir = Path.Combine(_regDir, "shared", "Microsoft.NETCore.App"); - - _cwdSharedUberFxBaseDir = Path.Combine(_currentWorkingDir, "shared", "Microsoft.UberFramework"); - _userSharedUberFxBaseDir = Path.Combine(_userDir, ".dotnet", RepoDirectories.BuildArchitecture, "shared", "Microsoft.UberFramework"); - _exeSharedUberFxBaseDir = Path.Combine(_exeDir, "shared", "Microsoft.UberFramework"); - _regSharedUberFxBaseDir = Path.Combine(_regDir, "shared", "Microsoft.UberFramework"); - - // Create directories. It's necessary to copy the entire publish folder to the exe dir because - // we'll need to build from it. The CopyDirectory method automatically creates the dest dir - Directory.CreateDirectory(_cwdSharedFxBaseDir); - Directory.CreateDirectory(_userSharedFxBaseDir); - Directory.CreateDirectory(_regSharedFxBaseDir); - Directory.CreateDirectory(_cwdSharedUberFxBaseDir); - Directory.CreateDirectory(_userSharedUberFxBaseDir); - Directory.CreateDirectory(_regSharedUberFxBaseDir); - SharedFramework.CopyDirectory(_builtDotnet, _exeDir); - - //Copy dotnet to self-registered directory - File.Copy( - Path.Combine(_builtDotnet, RuntimeInformationExtensions.GetExeFileNameForCurrentPlatform("dotnet")), - Path.Combine(_regDir, RuntimeInformationExtensions.GetExeFileNameForCurrentPlatform("dotnet")), - true); - - // Restore and build SharedFxLookupPortableApp from exe dir - SharedFxLookupPortableAppFixture = new TestProjectFixture("SharedFxLookupPortableApp", RepoDirectories) - .EnsureRestored() - .BuildProject(); - var fixture = SharedFxLookupPortableAppFixture; - - // The actual framework version can be obtained from the built fixture. We'll use it to - // locate the builtSharedFxDir from which we can get the files contained in the version folder - string greatestVersionSharedFxPath = fixture.BuiltDotnet.GreatestVersionSharedFxPath; - _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); - - // Trace messages used to identify from which folder the framework was picked - _hostPolicyDllName = Path.GetFileName(fixture.TestProject.HostPolicyDll); - _exeSelectedMessage = $"The expected {_hostPolicyDllName} directory is [{_exeSharedFxBaseDir}"; - _regSelectedMessage = $"The expected {_hostPolicyDllName} directory is [{_regSharedFxBaseDir}"; - - _exeFoundUberFxMessage = $"Chose FX version [{_exeSharedUberFxBaseDir}"; - - _testOnlyProductBehaviorMarker = TestOnlyProductBehavior.Enable(fixture.BuiltDotnet.GreatestVersionHostFxrFilePath); - } - - public void Dispose() - { - _testOnlyProductBehaviorMarker?.Dispose(); - - SharedFxLookupPortableAppFixture.Dispose(); - - if (!TestProject.PreserveTestRuns()) - { - Directory.Delete(_multilevelDir, true); - } - } - - [Fact] - [PlatformSpecific(TestPlatforms.Windows)] // Multi-level lookup is only supported on Windows. - public void SharedMultilevelFxLookup_Must_Verify_Folders_in_the_Correct_Order() - { - var fixture = SharedFxLookupPortableAppFixture - .Copy(); - - var dotnet = fixture.BuiltDotnet; - var appDll = fixture.TestProject.AppDll; - - // Set desired version = 9999.0.0 - string runtimeConfig = Path.Combine(fixture.TestProject.OutputDirectory, "SharedFxLookupPortableApp.runtimeconfig.json"); - SharedFramework.SetRuntimeConfigJson(runtimeConfig, "9999.0.0"); - - // Add version in the reg dir - SharedFramework.AddAvailableSharedFxVersions(_builtSharedFxDir, _regSharedFxBaseDir, "9999.0.0"); - - // Version: 9999.0.0 - // Cwd: empty - // User: empty - // Exe: empty - // Reg: 9999.0.0 - // Expected: 9999.0.0 from reg dir - dotnet.Exec(appDll) - .WorkingDirectory(_currentWorkingDir) - .WithUserProfile(_userDir) - .EnvironmentVariable("COREHOST_TRACE", "1") - .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "1") - .EnvironmentVariable(Constants.TestOnlyEnvironmentVariables.GloballyRegisteredPath, _regDir) - .CaptureStdOut() - .CaptureStdErr() - .Execute() - .Should().Pass() - .And.HaveStdErrContaining(Path.Combine(_regSelectedMessage, "9999.0.0")); - - // Add a dummy version in the user dir - SharedFramework.AddAvailableSharedFxVersions(_builtSharedFxDir, _userSharedFxBaseDir, "9999.0.0"); - - // Version: 9999.0.0 - // Cwd: empty - // User: 9999.0.0 --> should not be picked - // Exe: empty - // Reg: 9999.0.0 - // Expected: 9999.0.0 from reg dir - dotnet.Exec(appDll) - .WorkingDirectory(_currentWorkingDir) - .WithUserProfile(_userDir) - .EnvironmentVariable("COREHOST_TRACE", "1") - .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "1") - .EnvironmentVariable(Constants.TestOnlyEnvironmentVariables.GloballyRegisteredPath, _regDir) - .CaptureStdOut() - .CaptureStdErr() - .Execute() - .Should().Pass() - .And.HaveStdErrContaining(Path.Combine(_regSelectedMessage, "9999.0.0")); - - // Add a dummy version in the cwd dir - SharedFramework.AddAvailableSharedFxVersions(_builtSharedFxDir, _cwdSharedFxBaseDir, "9999.0.0"); - - // Version: 9999.0.0 - // Cwd: 9999.0.0 --> should not be picked - // User: 9999.0.0 --> should not be picked - // Exe: empty - // Reg: 9999.0.0 - // Expected: 9999.0.0 from reg dir - dotnet.Exec(appDll) - .WorkingDirectory(_currentWorkingDir) - .WithUserProfile(_userDir) - .EnvironmentVariable("COREHOST_TRACE", "1") - .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "1") - .EnvironmentVariable(Constants.TestOnlyEnvironmentVariables.GloballyRegisteredPath, _regDir) - .CaptureStdOut() - .CaptureStdErr() - .Execute() - .Should().Pass() - .And.HaveStdErrContaining(Path.Combine(_regSelectedMessage, "9999.0.0")); - - // Add version in the exe dir - SharedFramework.AddAvailableSharedFxVersions(_builtSharedFxDir, _exeSharedFxBaseDir, "9999.0.0"); - - // Version: 9999.0.0 - // Cwd: 9999.0.0 --> should not be picked - // User: 9999.0.0 --> should not be picked - // Exe: 9999.0.0 - // Reg: 9999.0.0 - // Expected: 9999.0.0 from exe dir - dotnet.Exec(appDll) - .WorkingDirectory(_currentWorkingDir) - .WithUserProfile(_userDir) - .EnvironmentVariable("COREHOST_TRACE", "1") - .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "1") - .EnvironmentVariable(Constants.TestOnlyEnvironmentVariables.GloballyRegisteredPath, _regDir) - .CaptureStdOut() - .CaptureStdErr() - .Execute() - .Should().Pass() - .And.HaveStdErrContaining(Path.Combine(_exeSelectedMessage, "9999.0.0")); - - // Verify we have the expected runtime versions - dotnet.Exec("--list-runtimes") - .WorkingDirectory(_currentWorkingDir) - .WithUserProfile(_userDir) - .EnvironmentVariable("COREHOST_TRACE", "1") - .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "1") - .EnvironmentVariable(Constants.TestOnlyEnvironmentVariables.GloballyRegisteredPath, _regDir) - .CaptureStdOut() - .CaptureStdErr() - .Execute() - .Should().Pass() - .And.HaveStdOutContaining("Microsoft.NETCore.App 9999.0.0"); - } - - [Fact] - [PlatformSpecific(TestPlatforms.Windows)] // Multi-level lookup is only supported on Windows. - public void SharedMultilevelFxLookup_Must_Not_Roll_Forward_If_Framework_Version_Is_Specified_Through_Argument() - { - var fixture = SharedFxLookupPortableAppFixture - .Copy(); - - var dotnet = fixture.BuiltDotnet; - var appDll = fixture.TestProject.AppDll; - - // Add some dummy versions - SharedFramework.AddAvailableSharedFxVersions(_builtSharedFxDir, _exeSharedFxBaseDir, "9999.0.0", "9999.0.1", "9999.0.0-dummy2", "9999.0.4"); - SharedFramework.AddAvailableSharedFxVersions(_builtSharedFxDir, _regSharedFxBaseDir, "9999.0.0", "9999.0.2", "9999.0.3", "9999.0.0-dummy3"); - - // Version: 9999.0.0 (through --fx-version arg) - // Cwd: empty - // User: empty - // Exe: 9999.0.0, 9999.0.1, 9999.0.4, 9999.0.0-dummy2 - // Reg: 9999.0.0, 9999.0.2, 9999.0.3, 9999.0.0-dummy3 - // Expected: 9999.0.1 from exe dir - dotnet.Exec("--fx-version", "9999.0.1", appDll) - .WorkingDirectory(_currentWorkingDir) - .WithUserProfile(_userDir) - .EnvironmentVariable("COREHOST_TRACE", "1") - .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "1") - .EnvironmentVariable(Constants.TestOnlyEnvironmentVariables.GloballyRegisteredPath, _regDir) - .CaptureStdOut() - .CaptureStdErr() - .Execute() - .Should().Pass() - .And.HaveStdErrContaining(Path.Combine(_exeSelectedMessage, "9999.0.1")); - - // Version: 9999.0.0-dummy1 (through --fx-version arg) - // Cwd: empty - // User: empty - // Exe: 9999.0.0, 9999.0.1, 9999.0.4, 9999.0.0-dummy2 - // Reg: 9999.0.0, 9999.0.2, 9999.0.3, 9999.0.0-dummy3 - // Expected: no compatible version - dotnet.Exec("--fx-version", "9999.0.0-dummy1", appDll) - .WorkingDirectory(_currentWorkingDir) - .WithUserProfile(_userDir) - .EnvironmentVariable("COREHOST_TRACE", "1") - .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "1") - .EnvironmentVariable(Constants.TestOnlyEnvironmentVariables.GloballyRegisteredPath, _regDir) - .CaptureStdOut() - .CaptureStdErr() - .Execute(fExpectedToFail: true) - .Should().Fail() - .And.HaveStdErrContaining("It was not possible to find any compatible framework version"); - - // Version: 9999.0.0 (through --fx-version arg) - // Cwd: empty - // User: empty - // Exe: 9999.0.0, 9999.0.1, 9999.0.4, 9999.0.0-dummy2 - // Reg: 9999.0.0, 9999.0.2, 9999.0.3, 9999.0.0-dummy3 - // Expected: 9999.0.2 from reg dir - dotnet.Exec("--fx-version", "9999.0.2", appDll) - .WorkingDirectory(_currentWorkingDir) - .WithUserProfile(_userDir) - .EnvironmentVariable("COREHOST_TRACE", "1") - .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "1") - .EnvironmentVariable(Constants.TestOnlyEnvironmentVariables.GloballyRegisteredPath, _regDir) - .CaptureStdOut() - .CaptureStdErr() - .Execute() - .Should().Pass() - .And.HaveStdErrContaining(Path.Combine(_regSelectedMessage, "9999.0.2")); - - // Version: 9999.0.0 (through --fx-version arg) - // Cwd: empty - // User: empty - // Exe: 9999.0.0, 9999.0.1, 9999.0.4, 9999.0.0-dummy2 - // Reg: 9999.0.0, 9999.0.2, 9999.0.3, 9999.0.0-dummy3 - // Expected: 9999.0.0 from exe dir - dotnet.Exec("--fx-version", "9999.0.0", appDll) - .WorkingDirectory(_currentWorkingDir) - .WithUserProfile(_userDir) - .EnvironmentVariable("COREHOST_TRACE", "1") - .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "1") - .EnvironmentVariable(Constants.TestOnlyEnvironmentVariables.GloballyRegisteredPath, _regDir) - .CaptureStdOut() - .CaptureStdErr() - .Execute() - .Should().Pass() - .And.HaveStdErrContaining(Path.Combine(_exeSelectedMessage, "9999.0.0")); - - // Verify we have the expected runtime versions - dotnet.Exec("--list-runtimes") - .WorkingDirectory(_currentWorkingDir) - .WithUserProfile(_userDir) - .EnvironmentVariable("COREHOST_TRACE", "1") - .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "1") - .EnvironmentVariable(Constants.TestOnlyEnvironmentVariables.GloballyRegisteredPath, _regDir) - .CaptureStdOut() - .CaptureStdErr() - .Execute() - .Should().Pass() - .And.HaveStdOutContaining("Microsoft.NETCore.App 9999.0.0") - .And.HaveStdOutContaining("Microsoft.NETCore.App 9999.0.0-dummy2") - .And.HaveStdOutContaining("Microsoft.NETCore.App 9999.0.2") - .And.HaveStdOutContaining("Microsoft.NETCore.App 9999.0.3") - .And.HaveStdOutContaining("Microsoft.NETCore.App 9999.0.0-dummy3"); - } - } -} diff --git a/src/installer/tests/HostActivation.Tests/SDKLookup.cs b/src/installer/tests/HostActivation.Tests/SDKLookup.cs index b587a019edabdf..7ed2b99d4af0d9 100644 --- a/src/installer/tests/HostActivation.Tests/SDKLookup.cs +++ b/src/installer/tests/HostActivation.Tests/SDKLookup.cs @@ -1,77 +1,38 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using Microsoft.DotNet.Cli.Build; using System; using System.Collections.Generic; using System.IO; +using Microsoft.DotNet.Cli.Build; +using Microsoft.DotNet.Cli.Build.Framework; using Xunit; namespace Microsoft.DotNet.CoreSetup.Test.HostActivation { - public class SDKLookup : IDisposable + public class SDKLookup : IClassFixture { - private static readonly IDictionary s_DefaultEnvironment = new Dictionary() - { - {"COREHOST_TRACE", "1" }, - // The SDK being used may be crossgen'd for a different architecture than we are building for. - // Turn off ready to run, so an x64 crossgen'd SDK can be loaded in an x86 process. - {"COMPlus_ReadyToRun", "0" }, - }; - - private readonly RepoDirectoriesProvider RepoDirectories; - private readonly DotNetCli DotNet; - - private readonly string _baseDir; - private readonly string _currentWorkingDir; - private readonly string _userDir; - private readonly string _executableDir; - private readonly string _cwdSdkBaseDir; - private readonly string _userSdkBaseDir; - private readonly string _exeSdkBaseDir; - private readonly string _exeSelectedMessage; - - private const string _dotnetSdkDllMessageTerminator = "dotnet.dll]"; - - public SDKLookup() - { - // The dotnetSDKLookup dir will contain some folders and files that will be - // necessary to perform the tests - string baseDir = Path.Combine(TestArtifact.TestArtifactsPath, "dotnetSDKLookup"); - _baseDir = SharedFramework.CalculateUniqueTestDirectory(baseDir); - - // The three tested locations will be the cwd, the user folder and the exe dir. cwd and user are no longer supported. - // All dirs will be placed inside the base folder - _currentWorkingDir = Path.Combine(_baseDir, "cwd"); - _userDir = Path.Combine(_baseDir, "user"); - _executableDir = Path.Combine(_baseDir, "exe"); - - DotNet = new DotNetBuilder(_baseDir, Path.Combine(TestArtifact.TestArtifactsPath, "sharedFrameworkPublish"), "exe") - .AddMicrosoftNETCoreAppFrameworkMockHostPolicy("9999.0.0") - .Build(); + private SharedTestState SharedState { get; } - RepoDirectories = new RepoDirectoriesProvider(builtDotnet: DotNet.BinPath); + readonly DotNetCli ExecutableDotNet; + readonly DotNetBuilder ExecutableDotNetBuilder; + string ExecutableSelectedMessage { get; } - // SdkBaseDirs contain all available version folders - _cwdSdkBaseDir = Path.Combine(_currentWorkingDir, "sdk"); - _userSdkBaseDir = Path.Combine(_userDir, ".dotnet", RepoDirectories.BuildArchitecture, "sdk"); - _exeSdkBaseDir = Path.Combine(_executableDir, "sdk"); + public SDKLookup(SharedTestState sharedState) + { + SharedState = sharedState; - // Create directories - Directory.CreateDirectory(_cwdSdkBaseDir); - Directory.CreateDirectory(_userSdkBaseDir); - Directory.CreateDirectory(_exeSdkBaseDir); + string exeDotNetPath = SharedFramework.CalculateUniqueTestDirectory(sharedState.BaseDir); + ExecutableDotNetBuilder = new DotNetBuilder(exeDotNetPath, sharedState.BuiltDotNet.BinPath, "exe"); + ExecutableDotNet = ExecutableDotNetBuilder + .AddMicrosoftNETCoreAppFrameworkMockHostPolicy("9999.0.0") + .Build(); // Trace messages used to identify from which folder the SDK was picked - _exeSelectedMessage = $"Using .NET SDK dll=[{_exeSdkBaseDir}"; - } + ExecutableSelectedMessage = $"Using .NET SDK dll=[{Path.Combine(ExecutableDotNet.BinPath, "sdk")}"; - public void Dispose() - { - if (!TestArtifact.PreserveTestRuns()) - { - Directory.Delete(_baseDir, true); - } + // Note: no need to delete the directory, it will be removed once the entire class is done + // since everything is under the BaseDir from the shared state } [Fact] @@ -83,14 +44,7 @@ public void SdkLookup_Global_Json_Single_Digit_Patch_Rollup() // Specified SDK version: 9999.3.4-global-dummy // Exe: empty // Expected: no compatible version and a specific error messages - DotNet.Exec("help") - .WorkingDirectory(_currentWorkingDir) - .WithUserProfile(_userDir) - .Environment(s_DefaultEnvironment) - .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "0") - .CaptureStdOut() - .CaptureStdErr() - .Execute(fExpectedToFail: true) + RunTest() .Should().Fail() .And.HaveStdErrContaining("A compatible installed .NET SDK for global.json version") .And.HaveStdErrContaining("It was not possible to find any installed .NET SDKs") @@ -98,117 +52,69 @@ public void SdkLookup_Global_Json_Single_Digit_Patch_Rollup() .And.NotHaveStdErrContaining("Checking if resolved SDK dir"); // Add SDK versions - AddAvailableSdkVersions(_exeSdkBaseDir, "9999.4.1", "9999.3.4-dummy"); + AddAvailableSdkVersions("9999.4.1", "9999.3.4-dummy"); // Specified SDK version: 9999.3.4-global-dummy // Exe: 9999.4.1, 9999.3.4-dummy // Expected: no compatible version and a specific error message - DotNet.Exec("help") - .WorkingDirectory(_currentWorkingDir) - .WithUserProfile(_userDir) - .Environment(s_DefaultEnvironment) - .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "0") - .CaptureStdOut() - .CaptureStdErr() - .Execute(fExpectedToFail: true) + RunTest() .Should().Fail() .And.HaveStdErrContaining("A compatible installed .NET SDK for global.json version") .And.NotHaveStdErrContaining("It was not possible to find any installed .NET SDKs"); // Add SDK versions - AddAvailableSdkVersions(_exeSdkBaseDir, "9999.3.3"); + AddAvailableSdkVersions("9999.3.3"); // Specified SDK version: 9999.3.4-global-dummy // Exe: 9999.4.1, 9999.3.4-dummy, 9999.3.3 // Expected: no compatible version and a specific error message - DotNet.Exec("help") - .WorkingDirectory(_currentWorkingDir) - .WithUserProfile(_userDir) - .Environment(s_DefaultEnvironment) - .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "0") - .CaptureStdOut() - .CaptureStdErr() - .Execute(fExpectedToFail: true) + RunTest() .Should().Fail() .And.HaveStdErrContaining("A compatible installed .NET SDK for global.json version") .And.NotHaveStdErrContaining("It was not possible to find any installed .NET SDKs"); // Add SDK versions - AddAvailableSdkVersions(_exeSdkBaseDir, "9999.3.4"); + AddAvailableSdkVersions("9999.3.4"); // Specified SDK version: 9999.3.4-global-dummy // Exe: 9999.4.1, 9999.3.4-dummy, 9999.3.3, 9999.3.4 // Expected: 9999.3.4 from exe dir - DotNet.Exec("help") - .WorkingDirectory(_currentWorkingDir) - .WithUserProfile(_userDir) - .Environment(s_DefaultEnvironment) - .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "0") - .CaptureStdOut() - .CaptureStdErr() - .Execute() + RunTest() .Should().Pass() - .And.HaveStdErrContaining(Path.Combine(_exeSelectedMessage, "9999.3.4", _dotnetSdkDllMessageTerminator)); + .And.HaveStdErrContaining(ExpectedResolvedSdkOutput("9999.3.4")); // Add SDK versions - AddAvailableSdkVersions(_exeSdkBaseDir, "9999.3.5-dummy"); + AddAvailableSdkVersions("9999.3.5-dummy"); // Specified SDK version: 9999.3.4-global-dummy // Exe: 9999.4.1, 9999.3.4-dummy, 9999.3.3, 9999.3.4, 9999.3.5-dummy // Expected: 9999.3.5-dummy from exe dir - DotNet.Exec("help") - .WorkingDirectory(_currentWorkingDir) - .WithUserProfile(_userDir) - .Environment(s_DefaultEnvironment) - .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "0") - .CaptureStdOut() - .CaptureStdErr() - .Execute() + RunTest() .Should().Pass() - .And.HaveStdErrContaining(Path.Combine(_exeSelectedMessage, "9999.3.5-dummy", _dotnetSdkDllMessageTerminator)); + .And.HaveStdErrContaining(ExpectedResolvedSdkOutput("9999.3.5-dummy")); // Add SDK versions - AddAvailableSdkVersions(_exeSdkBaseDir, "9999.3.600"); + AddAvailableSdkVersions("9999.3.600"); // Specified SDK version: 9999.3.4-global-dummy // Exe: 9999.4.1, 9999.3.4-dummy, 9999.3.3, 9999.3.4, 9999.3.5-dummy, 9999.3.600 // Expected: 9999.3.5-dummy from exe dir - DotNet.Exec("help") - .WorkingDirectory(_currentWorkingDir) - .WithUserProfile(_userDir) - .Environment(s_DefaultEnvironment) - .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "0") - .CaptureStdOut() - .CaptureStdErr() - .Execute() + RunTest() .Should().Pass() - .And.HaveStdErrContaining(Path.Combine(_exeSelectedMessage, "9999.3.5-dummy", _dotnetSdkDllMessageTerminator)); + .And.HaveStdErrContaining(ExpectedResolvedSdkOutput("9999.3.5-dummy")); // Add SDK versions - AddAvailableSdkVersions(_exeSdkBaseDir, "9999.3.4-global-dummy"); + AddAvailableSdkVersions("9999.3.4-global-dummy"); // Specified SDK version: 9999.3.4-global-dummy // Exe: 9999.4.1, 9999.3.4-dummy, 9999.3.3, 9999.3.4, 9999.3.5-dummy, 9999.3.600, 9999.3.4-global-dummy // Expected: 9999.3.4-global-dummy from exe dir - DotNet.Exec("help") - .WorkingDirectory(_currentWorkingDir) - .WithUserProfile(_userDir) - .Environment(s_DefaultEnvironment) - .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "0") - .CaptureStdOut() - .CaptureStdErr() - .Execute() + RunTest() .Should().Pass() - .And.HaveStdErrContaining(Path.Combine(_exeSelectedMessage, "9999.3.4-global-dummy", _dotnetSdkDllMessageTerminator)); + .And.HaveStdErrContaining(ExpectedResolvedSdkOutput("9999.3.4-global-dummy")); // Verify we have the expected SDK versions - DotNet.Exec("--list-sdks") - .WorkingDirectory(_currentWorkingDir) - .WithUserProfile(_userDir) - .Environment(s_DefaultEnvironment) - .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "0") - .CaptureStdOut() - .Execute() + RunTest("--list-sdks") .Should().Pass() .And.HaveStdOutContaining("9999.3.4-dummy") .And.HaveStdOutContaining("9999.3.4-global-dummy") @@ -228,130 +134,75 @@ public void SdkLookup_Global_Json_Two_Part_Patch_Rollup() // Specified SDK version: 9999.3.304-global-dummy // Exe: empty // Expected: no compatible version and a specific error messages - DotNet.Exec("help") - .WorkingDirectory(_currentWorkingDir) - .WithUserProfile(_userDir) - .Environment(s_DefaultEnvironment) - .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "0") - .CaptureStdOut() - .CaptureStdErr() - .Execute(fExpectedToFail: true) + RunTest() .Should().Fail() .And.HaveStdErrContaining("A compatible installed .NET SDK for global.json version") .And.HaveStdErrContaining("It was not possible to find any installed .NET SDKs"); // Add SDK versions - AddAvailableSdkVersions(_exeSdkBaseDir, "9999.3.57", "9999.3.4-dummy"); + AddAvailableSdkVersions("9999.3.57", "9999.3.4-dummy"); // Specified SDK version: 9999.3.304-global-dummy // Exe: 9999.3.57, 9999.3.4-dummy // Expected: no compatible version and a specific error message - DotNet.Exec("help") - .WorkingDirectory(_currentWorkingDir) - .WithUserProfile(_userDir) - .Environment(s_DefaultEnvironment) - .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "0") - .CaptureStdOut() - .CaptureStdErr() - .Execute(fExpectedToFail: true) + RunTest() .Should().Fail() .And.HaveStdErrContaining("A compatible installed .NET SDK for global.json version") .And.NotHaveStdErrContaining("It was not possible to find any installed .NET SDKs"); // Add SDK versions - AddAvailableSdkVersions(_exeSdkBaseDir, "9999.3.300", "9999.7.304-global-dummy"); + AddAvailableSdkVersions("9999.3.300", "9999.7.304-global-dummy"); // Specified SDK version: 9999.3.304-global-dummy // Exe: 9999.3.57, 9999.3.4-dummy, 9999.3.300, 9999.7.304-global-dummy // Expected: no compatible version and a specific error message - DotNet.Exec("help") - .WorkingDirectory(_currentWorkingDir) - .WithUserProfile(_userDir) - .Environment(s_DefaultEnvironment) - .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "0") - .CaptureStdOut() - .CaptureStdErr() - .Execute(fExpectedToFail: true) + RunTest() .Should().Fail() .And.HaveStdErrContaining("A compatible installed .NET SDK for global.json version") .And.NotHaveStdErrContaining("It was not possible to find any installed .NET SDKs"); // Add SDK versions - AddAvailableSdkVersions(_exeSdkBaseDir, "9999.3.304"); + AddAvailableSdkVersions("9999.3.304"); // Specified SDK version: 9999.3.304-global-dummy // Exe: 99999.3.57, 9999.3.4-dummy, 9999.3.300, 9999.7.304-global-dummy, 9999.3.304 // Expected: 9999.3.304 from exe dir - DotNet.Exec("help") - .WorkingDirectory(_currentWorkingDir) - .WithUserProfile(_userDir) - .Environment(s_DefaultEnvironment) - .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "0") - .CaptureStdOut() - .CaptureStdErr() - .Execute() + RunTest() .Should().Pass() - .And.HaveStdErrContaining(Path.Combine(_exeSelectedMessage, "9999.3.304", _dotnetSdkDllMessageTerminator)); + .And.HaveStdErrContaining(ExpectedResolvedSdkOutput("9999.3.304")); // Add SDK versions - AddAvailableSdkVersions(_exeSdkBaseDir, "9999.3.399", "9999.3.399-dummy", "9999.3.400"); + AddAvailableSdkVersions("9999.3.399", "9999.3.399-dummy", "9999.3.400"); // Specified SDK version: 9999.3.304-global-dummy // Exe: 9999.3.57, 9999.3.4-dummy, 9999.3.300, 9999.7.304-global-dummy, 9999.3.304, 9999.3.399, 9999.3.399-dummy, 9999.3.400 // Expected: 9999.3.399 from exe dir - DotNet.Exec("help") - .WorkingDirectory(_currentWorkingDir) - .WithUserProfile(_userDir) - .Environment(s_DefaultEnvironment) - .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "0") - .CaptureStdOut() - .CaptureStdErr() - .Execute() + RunTest() .Should().Pass() - .And.HaveStdErrContaining(Path.Combine(_exeSelectedMessage, "9999.3.399", _dotnetSdkDllMessageTerminator)); + .And.HaveStdErrContaining(ExpectedResolvedSdkOutput("9999.3.399")); // Add SDK versions - AddAvailableSdkVersions(_exeSdkBaseDir, "9999.3.2400", "9999.3.3004"); + AddAvailableSdkVersions("9999.3.2400", "9999.3.3004"); // Specified SDK version: 9999.3.304-global-dummy // Exe: 9999.3.57, 9999.3.4-dummy, 9999.3.300, 9999.7.304-global-dummy, 9999.3.304, 9999.3.399, 9999.3.399-dummy, 9999.3.400, 9999.3.2400, 9999.3.3004 // Expected: 9999.3.399 from exe dir - DotNet.Exec("help") - .WorkingDirectory(_currentWorkingDir) - .WithUserProfile(_userDir) - .Environment(s_DefaultEnvironment) - .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "0") - .CaptureStdOut() - .CaptureStdErr() - .Execute() + RunTest() .Should().Pass() - .And.HaveStdErrContaining(Path.Combine(_exeSelectedMessage, "9999.3.399", _dotnetSdkDllMessageTerminator)); + .And.HaveStdErrContaining(ExpectedResolvedSdkOutput("9999.3.399")); // Add SDK versions - AddAvailableSdkVersions(_exeSdkBaseDir, "9999.3.304-global-dummy"); + AddAvailableSdkVersions("9999.3.304-global-dummy"); // Specified SDK version: 9999.3.304-global-dummy // Exe: 9999.3.57, 9999.3.4-dummy, 9999.3.300, 9999.7.304-global-dummy, 9999.3.304, 9999.3.399, 9999.3.399-dummy, 9999.3.400, 9999.3.2400, 9999.3.3004, 9999.3.304-global-dummy // Expected: 9999.3.304-global-dummy from exe dir - DotNet.Exec("help") - .WorkingDirectory(_currentWorkingDir) - .WithUserProfile(_userDir) - .Environment(s_DefaultEnvironment) - .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "0") - .CaptureStdOut() - .CaptureStdErr() - .Execute() + RunTest() .Should().Pass() - .And.HaveStdErrContaining(Path.Combine(_exeSelectedMessage, "9999.3.304-global-dummy", _dotnetSdkDllMessageTerminator)); + .And.HaveStdErrContaining(ExpectedResolvedSdkOutput("9999.3.304-global-dummy")); // Verify we have the expected SDK versions - DotNet.Exec("--list-sdks") - .WorkingDirectory(_currentWorkingDir) - .WithUserProfile(_userDir) - .Environment(s_DefaultEnvironment) - .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "0") - .CaptureStdOut() - .Execute() + RunTest("--list-sdks") .Should().Pass() .And.HaveStdOutContaining("9999.3.57") .And.HaveStdOutContaining("9999.3.4-dummy") @@ -372,48 +223,28 @@ public void SdkLookup_Negative_Version() WriteEmptyGlobalJson(); // Add a negative SDK version - AddAvailableSdkVersions(_exeSdkBaseDir, "-1.-1.-1"); + AddAvailableSdkVersions("-1.-1.-1"); // Specified SDK version: none // Exe: -1.-1.-1 // Expected: no compatible version and a specific error messages - DotNet.Exec("help") - .WorkingDirectory(_currentWorkingDir) - .WithUserProfile(_userDir) - .Environment(s_DefaultEnvironment) - .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "0") - .CaptureStdOut() - .CaptureStdErr() - .Execute(fExpectedToFail: true) + RunTest() .Should().Fail() .And.HaveStdErrContaining("It was not possible to find any installed .NET SDKs") .And.HaveStdErrContaining("Install a .NET SDK from"); // Add SDK versions - AddAvailableSdkVersions(_exeSdkBaseDir, "9999.0.4"); + AddAvailableSdkVersions("9999.0.4"); // Specified SDK version: none // Exe: -1.-1.-1, 9999.0.4 // Expected: 9999.0.4 from exe dir - DotNet.Exec("help") - .WorkingDirectory(_currentWorkingDir) - .WithUserProfile(_userDir) - .Environment(s_DefaultEnvironment) - .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "0") - .CaptureStdOut() - .CaptureStdErr() - .Execute() + RunTest() .Should().Pass() - .And.HaveStdErrContaining(Path.Combine(_exeSelectedMessage, "9999.0.4", _dotnetSdkDllMessageTerminator)); + .And.HaveStdErrContaining(ExpectedResolvedSdkOutput("9999.0.4")); // Verify we have the expected SDK versions - DotNet.Exec("--list-sdks") - .WorkingDirectory(_currentWorkingDir) - .WithUserProfile(_userDir) - .Environment(s_DefaultEnvironment) - .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "0") - .CaptureStdOut() - .Execute() + RunTest("--list-sdks") .Should().Pass() .And.HaveStdOutContaining("9999.0.4"); } @@ -424,129 +255,79 @@ public void SdkLookup_Must_Pick_The_Highest_Semantic_Version() WriteEmptyGlobalJson(); // Add SDK versions - AddAvailableSdkVersions(_exeSdkBaseDir, "9999.0.0", "9999.0.3-dummy.9", "9999.0.3-dummy.10"); + AddAvailableSdkVersions("9999.0.0", "9999.0.3-dummy.9", "9999.0.3-dummy.10"); // Specified SDK version: none - // Cwd: empty - // User: empty + // Cwd: 10000.0.0 --> should not be picked + // User: 9999.0.200 --> should not be picked // Exe: 9999.0.0, 9999.0.3-dummy.9, 9999.0.3-dummy.10 // Expected: 9999.0.3-dummy.10 from exe dir - DotNet.Exec("help") - .WorkingDirectory(_currentWorkingDir) - .WithUserProfile(_userDir) - .Environment(s_DefaultEnvironment) - .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "0") - .CaptureStdOut() - .CaptureStdErr() - .Execute() + RunTest() .Should().Pass() - .And.HaveStdErrContaining(Path.Combine(_exeSelectedMessage, "9999.0.3-dummy.10", _dotnetSdkDllMessageTerminator)); + .And.HaveStdErrContaining(ExpectedResolvedSdkOutput("9999.0.3-dummy.10")); // Add SDK versions - AddAvailableSdkVersions(_exeSdkBaseDir, "9999.0.3"); + AddAvailableSdkVersions("9999.0.3"); // Specified SDK version: none - // Cwd: empty - // User: empty + // Cwd: 10000.0.0 --> should not be picked + // User: 9999.0.200 --> should not be picked // Exe: 9999.0.0, 9999.0.3-dummy.9, 9999.0.3-dummy.10, 9999.0.3 // Expected: 9999.0.3 from exe dir - DotNet.Exec("help") - .WorkingDirectory(_currentWorkingDir) - .WithUserProfile(_userDir) - .Environment(s_DefaultEnvironment) - .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "0") - .CaptureStdOut() - .CaptureStdErr() - .Execute() + RunTest() .Should().Pass() - .And.HaveStdErrContaining(Path.Combine(_exeSelectedMessage, "9999.0.3", _dotnetSdkDllMessageTerminator)); + .And.HaveStdErrContaining(ExpectedResolvedSdkOutput("9999.0.3")); // Add SDK versions - AddAvailableSdkVersions(_userSdkBaseDir, "9999.0.200"); - AddAvailableSdkVersions(_cwdSdkBaseDir, "10000.0.0"); - AddAvailableSdkVersions(_exeSdkBaseDir, "9999.0.100"); + AddAvailableSdkVersions("9999.0.100"); // Specified SDK version: none // Cwd: 10000.0.0 --> should not be picked // User: 9999.0.200 --> should not be picked // Exe: 9999.0.0, 9999.0.3-dummy.9, 9999.0.3-dummy.10, 9999.0.3, 9999.0.100 // Expected: 9999.0.100 from exe dir - DotNet.Exec("help") - .WorkingDirectory(_currentWorkingDir) - .WithUserProfile(_userDir) - .Environment(s_DefaultEnvironment) - .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "0") - .CaptureStdOut() - .CaptureStdErr() - .Execute() + RunTest() .Should().Pass() - .And.HaveStdErrContaining(Path.Combine(_exeSelectedMessage, "9999.0.100", _dotnetSdkDllMessageTerminator)); + .And.HaveStdErrContaining(ExpectedResolvedSdkOutput("9999.0.100")); // Add SDK versions - AddAvailableSdkVersions(_exeSdkBaseDir, "9999.0.80"); + AddAvailableSdkVersions("9999.0.80"); // Specified SDK version: none // Cwd: 10000.0.0 --> should not be picked // User: 9999.0.200 --> should not be picked // Exe: 9999.0.0, 9999.0.3-dummy.9, 9999.0.3-dummy.10, 9999.0.3, 9999.0.100, 9999.0.80 // Expected: 9999.0.100 from exe dir - DotNet.Exec("help") - .WorkingDirectory(_currentWorkingDir) - .WithUserProfile(_userDir) - .Environment(s_DefaultEnvironment) - .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "0") - .CaptureStdOut() - .CaptureStdErr() - .Execute() + RunTest() .Should().Pass() - .And.HaveStdErrContaining(Path.Combine(_exeSelectedMessage, "9999.0.100", _dotnetSdkDllMessageTerminator)); + .And.HaveStdErrContaining(ExpectedResolvedSdkOutput("9999.0.100")); // Add SDK versions - AddAvailableSdkVersions(_exeSdkBaseDir, "9999.0.5500000"); + AddAvailableSdkVersions("9999.0.5500000"); // Specified SDK version: none // Cwd: 10000.0.0 --> should not be picked // User: 9999.0.200 --> should not be picked // Exe: 9999.0.0, 9999.0.3-dummy.9, 9999.0.3-dummy.10, 9999.0.3, 9999.0.100, 9999.0.80, 9999.0.5500000 // Expected: 9999.0.5500000 from exe dir - DotNet.Exec("help") - .WorkingDirectory(_currentWorkingDir) - .WithUserProfile(_userDir) - .Environment(s_DefaultEnvironment) - .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "0") - .CaptureStdOut() - .CaptureStdErr() - .Execute() + RunTest() .Should().Pass() - .And.HaveStdErrContaining(Path.Combine(_exeSelectedMessage, "9999.0.5500000", _dotnetSdkDllMessageTerminator)); + .And.HaveStdErrContaining(ExpectedResolvedSdkOutput("9999.0.5500000")); // Add SDK versions - AddAvailableSdkVersions(_exeSdkBaseDir, "9999.0.52000000"); + AddAvailableSdkVersions("9999.0.52000000"); // Specified SDK version: none // Cwd: 10000.0.0 --> should not be picked // User: 9999.0.200 --> should not be picked // Exe: 9999.0.0, 9999.0.3-dummy.9, 9999.0.3-dummy.10, 9999.0.3, 9999.0.100, 9999.0.80, 9999.0.5500000, 9999.0.52000000 // Expected: 9999.0.52000000 from exe dir - DotNet.Exec("help") - .WorkingDirectory(_currentWorkingDir) - .WithUserProfile(_userDir) - .Environment(s_DefaultEnvironment) - .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "0") - .CaptureStdOut() - .CaptureStdErr() - .Execute() + RunTest() .Should().Pass() - .And.HaveStdErrContaining(Path.Combine(_exeSelectedMessage, "9999.0.52000000", _dotnetSdkDllMessageTerminator)); + .And.HaveStdErrContaining(ExpectedResolvedSdkOutput("9999.0.52000000")); // Verify we have the expected SDK versions - DotNet.Exec("--list-sdks") - .WorkingDirectory(_currentWorkingDir) - .WithUserProfile(_userDir) - .Environment(s_DefaultEnvironment) - .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "0") - .CaptureStdOut() - .Execute() + RunTest("--list-sdks") .Should().Pass() .And.HaveStdOutContaining("9999.0.0") .And.HaveStdOutContaining("9999.0.3-dummy.9") @@ -574,42 +355,26 @@ public void It_allows_case_insensitive_roll_forward_policy_names(string rollForw WriteEmptyGlobalJson(); - AddAvailableSdkVersions(_exeSdkBaseDir, Requested); + AddAvailableSdkVersions(Requested); WriteGlobalJson(FormatGlobalJson(policy: rollForward, version: Requested)); - DotNet.Exec("help") - .WorkingDirectory(_currentWorkingDir) - .WithUserProfile(_userDir) - .Environment(s_DefaultEnvironment) - .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "0") - .CaptureStdOut() - .CaptureStdErr() - .Execute() - .Should() - .Pass() - .And.HaveStdErrContaining(Path.Combine(_exeSelectedMessage, Requested, _dotnetSdkDllMessageTerminator)); + RunTest() + .Should().Pass() + .And.HaveStdErrContaining(ExpectedResolvedSdkOutput(Requested)); } [Theory] [MemberData(nameof(InvalidGlobalJsonData))] public void It_falls_back_to_latest_sdk_for_invalid_global_json(string globalJsonContents, string[] messages) { - AddAvailableSdkVersions(_exeSdkBaseDir, "9999.0.100", "9999.0.300-dummy.9", "9999.1.402"); + AddAvailableSdkVersions("9999.0.100", "9999.0.300-dummy.9", "9999.1.402"); WriteGlobalJson(globalJsonContents); - var expectation = DotNet.Exec("help") - .WorkingDirectory(_currentWorkingDir) - .WithUserProfile(_userDir) - .Environment(s_DefaultEnvironment) - .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "0") - .CaptureStdOut() - .CaptureStdErr() - .Execute() - .Should() - .Pass() - .And.HaveStdErrContaining(Path.Combine(_exeSelectedMessage, "9999.1.402", _dotnetSdkDllMessageTerminator)); + var expectation = RunTest() + .Should().Pass() + .And.HaveStdErrContaining(ExpectedResolvedSdkOutput("9999.1.402")); foreach (var message in messages) { @@ -621,20 +386,13 @@ public void It_falls_back_to_latest_sdk_for_invalid_global_json(string globalJso [MemberData(nameof(SdkRollForwardData))] public void It_rolls_forward_as_expected(string policy, string requested, bool allowPrerelease, string expected, string[] installed) { - AddAvailableSdkVersions(_exeSdkBaseDir, installed); + AddAvailableSdkVersions(installed); WriteGlobalJson(FormatGlobalJson(policy: policy, version: requested, allowPrerelease: allowPrerelease)); - var result = DotNet.Exec("help") - .WorkingDirectory(_currentWorkingDir) - .WithUserProfile(_userDir) - .Environment(s_DefaultEnvironment) - .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "0") - .CaptureStdOut() - .CaptureStdErr() - .Execute(); + var result = RunTest(); - var globalJson = Path.Combine(_currentWorkingDir, "global.json"); + var globalJson = Path.Combine(SharedState.CurrentWorkingDir, "global.json"); if (expected == null) { @@ -649,7 +407,7 @@ public void It_rolls_forward_as_expected(string policy, string requested, bool a result .Should() .Pass() - .And.HaveStdErrContaining($"SDK path resolved to [{Path.Combine(_exeSdkBaseDir, expected)}]"); + .And.HaveStdErrContaining($"SDK path resolved to [{Path.Combine(ExecutableDotNet.BinPath, "sdk", expected)}]"); } } @@ -673,21 +431,13 @@ public void It_uses_latest_stable_sdk_if_allow_prerelease_is_false() const string ExpectedVersion = "10000.1.106"; - AddAvailableSdkVersions(_exeSdkBaseDir, installed); + AddAvailableSdkVersions(installed); WriteGlobalJson(FormatGlobalJson(allowPrerelease: false)); - var result = DotNet.Exec("help") - .WorkingDirectory(_currentWorkingDir) - .WithUserProfile(_userDir) - .Environment(s_DefaultEnvironment) - .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "0") - .CaptureStdOut() - .CaptureStdErr() - .Execute() - .Should() - .Pass() - .And.HaveStdErrContaining($"SDK path resolved to [{Path.Combine(_exeSdkBaseDir, ExpectedVersion)}]"); + var result = RunTest() + .Should().Pass() + .And.HaveStdErrContaining($"SDK path resolved to [{Path.Combine(ExecutableDotNet.BinPath, "sdk", ExpectedVersion)}]"); } public static IEnumerable InvalidGlobalJsonData @@ -1248,31 +998,19 @@ public static IEnumerable SdkRollForwardData // This method adds a list of new sdk version folders in the specified directory. // The actual contents are 'fake' and the mininum required for SDK discovery. // The dotnet.runtimeconfig.json created uses a dummy framework version (9999.0.0) - private void AddAvailableSdkVersions(string sdkBaseDir, params string[] availableVersions) + private void AddAvailableSdkVersions(params string[] availableVersions) { - string dummyRuntimeConfig = Path.Combine(RepoDirectories.TestAssetsFolder, "TestUtils", - "SDKLookup", "dotnet.runtimeconfig.json"); - foreach (string version in availableVersions) { - string newSdkDir = Path.Combine(sdkBaseDir, version); - Directory.CreateDirectory(newSdkDir); - - // ./dotnet.dll - File.WriteAllText(Path.Combine(newSdkDir, "dotnet.dll"), string.Empty); - - // ./dotnet.runtimeconfig.json - string runtimeConfig = Path.Combine(newSdkDir, "dotnet.runtimeconfig.json"); - File.Copy(dummyRuntimeConfig, runtimeConfig, true); + ExecutableDotNetBuilder.AddMockSDK(version, "9999.0.0"); } } // Put a global.json file in the cwd in order to specify a CLI private void CopyGlobalJson(string globalJsonFileName) { - string destFile = Path.Combine(_currentWorkingDir, "global.json"); - string srcFile = Path.Combine(RepoDirectories.TestAssetsFolder, "TestUtils", - "SDKLookup", globalJsonFileName); + string destFile = Path.Combine(SharedState.CurrentWorkingDir, "global.json"); + string srcFile = Path.Combine(SharedState.TestAssetsPath, globalJsonFileName); File.Copy(srcFile, destFile, true); } @@ -1288,9 +1026,85 @@ private static string FormatGlobalJson(string version = null, string policy = nu private void WriteGlobalJson(string contents) { - File.WriteAllText(Path.Combine(_currentWorkingDir, "global.json"), contents); + File.WriteAllText(Path.Combine(SharedState.CurrentWorkingDir, "global.json"), contents); } private void WriteEmptyGlobalJson() => WriteGlobalJson("{}"); + + private string ExpectedResolvedSdkOutput(string expectedVersion) + => Path.Combine("Using .NET SDK dll=[", ExecutableDotNet.BinPath, "sdk", expectedVersion, "dotnet.dll]"); + + private CommandResult RunTest() => RunTest("help"); + + private CommandResult RunTest(string command) + { + return ExecutableDotNet.Exec(command) + .WorkingDirectory(SharedState.CurrentWorkingDir) + .WithUserProfile(SharedState.UserDir) + .EnableTracingAndCaptureOutputs() + .MultilevelLookup(false) + .Execute(); + } + + public class SharedTestState : IDisposable + { + private readonly RepoDirectoriesProvider RepoDirectories; + + public DotNetCli BuiltDotNet { get; } + + public string BaseDir { get; } + + public string CurrentWorkingDir { get; } + + public string UserDir { get; } + + public string TestAssetsPath { get; } + + public SharedTestState() + { + // The dotnetSDKLookup dir will contain some folders and files that will be + // necessary to perform the tests + string baseDir = Path.Combine(TestArtifact.TestArtifactsPath, "dotnetSDKLookup"); + BaseDir = SharedFramework.CalculateUniqueTestDirectory(baseDir); + + // The three tested locations will be the cwd, the user folder and the exe dir. cwd and user are no longer supported. + // All dirs will be placed inside the base folder + + BuiltDotNet = new DotNetCli(Path.Combine(TestArtifact.TestArtifactsPath, "sharedFrameworkPublish")); + + RepoDirectories = new RepoDirectoriesProvider(); + + // Executable location is created per test as each test adds a different set of SDK versions + + var currentWorkingSdk = new DotNetBuilder(BaseDir, BuiltDotNet.BinPath, "current") + .AddMockSDK("10000.0.0", "9999.0.0") + .Build(); + CurrentWorkingDir = currentWorkingSdk.BinPath; + + UserDir = Path.Combine(BaseDir, "user"); + var userSdk = new DotNetBuilder(Path.Combine(UserDir, ".dotnet", RepoDirectories.BuildArchitecture), BuiltDotNet.BinPath, null) + .AddMockSDK("9999.0.200", "9999.0.0") + .Build(); + + TestAssetsPath = Path.Combine(RepoDirectories.TestAssetsFolder, "TestUtils", "SDKLookup"); + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + if (!TestArtifact.PreserveTestRuns() && Directory.Exists(BaseDir)) + { + Directory.Delete(BaseDir, true); + } + } + } + } } } diff --git a/src/installer/tests/TestUtils/Command.cs b/src/installer/tests/TestUtils/Command.cs index 91f85be3584324..a4e46f8dc475bd 100644 --- a/src/installer/tests/TestUtils/Command.cs +++ b/src/installer/tests/TestUtils/Command.cs @@ -288,6 +288,12 @@ public Command EnvironmentVariable(string name, string value) return this; } + public Command RemoveEnvironmentVariable(string name) + { + Process.StartInfo.Environment.Remove(name); + return this; + } + public Command CaptureStdOut() { ThrowIfRunning(); diff --git a/src/installer/tests/TestUtils/CommandExtensions.cs b/src/installer/tests/TestUtils/CommandExtensions.cs index 38c4cbff3ba448..cfa42d1fab4acd 100644 --- a/src/installer/tests/TestUtils/CommandExtensions.cs +++ b/src/installer/tests/TestUtils/CommandExtensions.cs @@ -46,9 +46,12 @@ public static Command DotNetRoot(this Command command, string dotNetRoot, string .EnvironmentVariable(Constants.DotnetRoot.WindowsX86EnvironmentVariable, dotNetRoot); } - public static Command MultilevelLookup(this Command command, bool enable) + public static Command MultilevelLookup(this Command command, bool? enable) { - return command.EnvironmentVariable(Constants.MultilevelLookup.EnvironmentVariable, enable ? "1" : "0"); + if (enable.HasValue) + return command.EnvironmentVariable(Constants.MultilevelLookup.EnvironmentVariable, enable.Value ? "1" : "0"); + + return command.RemoveEnvironmentVariable(Constants.MultilevelLookup.EnvironmentVariable); } public static Command RuntimeId(this Command command, string rid) diff --git a/src/installer/tests/TestUtils/Constants.cs b/src/installer/tests/TestUtils/Constants.cs index e36df4bd2cdb39..c1c0c2e40e636f 100644 --- a/src/installer/tests/TestUtils/Constants.cs +++ b/src/installer/tests/TestUtils/Constants.cs @@ -33,6 +33,16 @@ public static class RollForwardSetting public const string Disable = "Disable"; } + public static class Tfm + { + public const string RuntimeConfigPropertyName = "tfm"; + public const string NetCoreApp30 = "netcoreapp3.0"; + public const string NetCoreApp31 = "netcoreapp3.1"; + public const string Net5 = "net5.0"; + public const string Net6 = "net6.0"; + public const string Net7 = "net7.0"; + } + public static class FxVersion { public const string CommandLineArgument = "--fx-version"; diff --git a/src/installer/tests/TestUtils/DotNetBuilder.cs b/src/installer/tests/TestUtils/DotNetBuilder.cs index d4cb58340ca6ed..53a61568d70747 100644 --- a/src/installer/tests/TestUtils/DotNetBuilder.cs +++ b/src/installer/tests/TestUtils/DotNetBuilder.cs @@ -21,7 +21,7 @@ public class DotNetBuilder public DotNetBuilder(string basePath, string builtDotnet, string name) { - _path = Path.Combine(basePath, name); + _path = name == null ? basePath : Path.Combine(basePath, name); Directory.CreateDirectory(_path); _repoDirectories = new RepoDirectoriesProvider(builtDotnet: _path); @@ -163,7 +163,8 @@ public DotNetBuilder AddMicrosoftNETCoreAppFrameworkMockCoreClr(string version, public DotNetBuilder AddFramework( string name, string version, - Action runtimeConfigCustomizer) + Action runtimeConfigCustomizer, + Action frameworkCustomizer = null) { // ./shared// - create a mock of effectively empty non-root framework string path = Path.Combine(_path, "shared", name, version); @@ -174,20 +175,23 @@ public DotNetBuilder AddFramework( runtimeConfigCustomizer(runtimeConfig); runtimeConfig.Save(); + if (frameworkCustomizer is not null) + frameworkCustomizer(path); + return this; } public DotNetBuilder AddMockSDK( - string version, - string MNAVersion) + string sdkVersion, + string runtimeVersion) { - string path = Path.Combine(_path, "sdk", version); + string path = Path.Combine(_path, "sdk", sdkVersion); Directory.CreateDirectory(path); using var _ = File.Create(Path.Combine(path, "dotnet.dll")); RuntimeConfig dotnetRuntimeConfig = new RuntimeConfig(Path.Combine(path, "dotnet.runtimeconfig.json")); - dotnetRuntimeConfig.WithFramework(new RuntimeConfig.Framework("Microsoft.NETCore.App", MNAVersion)); + dotnetRuntimeConfig.WithFramework(new RuntimeConfig.Framework("Microsoft.NETCore.App", runtimeVersion)); dotnetRuntimeConfig.Save(); return this; diff --git a/src/installer/tests/TestUtils/RuntimeConfig.cs b/src/installer/tests/TestUtils/RuntimeConfig.cs index 2a8418f4713280..809a5c9d09bbb1 100644 --- a/src/installer/tests/TestUtils/RuntimeConfig.cs +++ b/src/installer/tests/TestUtils/RuntimeConfig.cs @@ -97,6 +97,7 @@ internal static Framework FromJson(JObject jobject) private string _rollForward; private int? _rollForwardOnNoCandidateFx; private bool? _applyPatches; + private string _tfm; private readonly string _path; private readonly List _frameworks = new List(); private readonly List _includedFrameworks = new List(); @@ -227,6 +228,12 @@ public RuntimeConfig WithApplyPatches(bool? value) return this; } + public RuntimeConfig WithTfm(string tfm) + { + _tfm = tfm; + return this; + } + public RuntimeConfig WithProperty(string name, string value) { _properties.Add(new Tuple(name, value)); @@ -271,6 +278,13 @@ public void Save() _applyPatches.Value); } + if (_tfm is not null) + { + runtimeOptions.Add( + Constants.Tfm.RuntimeConfigPropertyName, + _tfm); + } + if (_properties.Count > 0) { JObject configProperties = new JObject(); diff --git a/src/native/corehost/test/mockhostpolicy/mockhostpolicy.cpp b/src/native/corehost/test/mockhostpolicy/mockhostpolicy.cpp index 3c54eb95f8f324..981673901ce1c1 100644 --- a/src/native/corehost/test/mockhostpolicy/mockhostpolicy.cpp +++ b/src/native/corehost/test/mockhostpolicy/mockhostpolicy.cpp @@ -77,9 +77,9 @@ SHARED_API int HOSTPOLICY_CALLTYPE corehost_load(host_interface_t* init) { std::cout << "mock frameworks: " << tostr(init->fx_names.arr[i]).data() << " " - << tostr(init->fx_found_versions.arr[i]).data() << " [requested: " - << tostr(init->fx_requested_versions.arr[i]).data() << "] [path: " - << tostr(init->fx_dirs.arr[i]).data() << "]" + << tostr(init->fx_found_versions.arr[i]).data() << " [path: " + << tostr(init->fx_dirs.arr[i]).data() << "] [requested: " + << tostr(init->fx_requested_versions.arr[i]).data() << "]" << std::endl; } } From 0765c1ca019c9070fcd0547aa91215120015967c Mon Sep 17 00:00:00 2001 From: vitek-karas <10670590+vitek-karas@users.noreply.github.com> Date: Wed, 9 Mar 2022 05:24:18 -0800 Subject: [PATCH 2/4] Remove "user" directory from SDK resolution tests The product simply doesn't have any knoweldge about user directory SDK lookup, so there's little value in testing for it (even though the test expects it to not have any impact). --- .../HostActivation.Tests/MultilevelSDKLookup.cs | 10 +--------- .../tests/HostActivation.Tests/SDKLookup.cs | 8 -------- src/installer/tests/TestUtils/Command.cs | 16 ---------------- 3 files changed, 1 insertion(+), 33 deletions(-) diff --git a/src/installer/tests/HostActivation.Tests/MultilevelSDKLookup.cs b/src/installer/tests/HostActivation.Tests/MultilevelSDKLookup.cs index 8ddb3dc4fea56d..64ea450ddc8b61 100644 --- a/src/installer/tests/HostActivation.Tests/MultilevelSDKLookup.cs +++ b/src/installer/tests/HostActivation.Tests/MultilevelSDKLookup.cs @@ -18,11 +18,9 @@ public class MultilevelSDKLookup : IDisposable private readonly DotNetCli DotNet; private readonly string _currentWorkingDir; - private readonly string _userDir; private readonly string _exeDir; private readonly string _regDir; private readonly string _cwdSdkBaseDir; - private readonly string _userSdkBaseDir; private readonly string _exeSdkBaseDir; private readonly string _regSdkBaseDir; private readonly string _exeSelectedMessage; @@ -43,7 +41,6 @@ public MultilevelSDKLookup() // The tested locations will be the cwd, user folder, exe dir, and registered directory. cwd and user are no longer supported. // All dirs will be placed inside the multilevel folder _currentWorkingDir = Path.Combine(_multilevelDir, "cwd"); - _userDir = Path.Combine(_multilevelDir, "user"); _exeDir = Path.Combine(_multilevelDir, "exe"); _regDir = Path.Combine(_multilevelDir, "reg"); @@ -55,13 +52,11 @@ public MultilevelSDKLookup() // SdkBaseDirs contain all available version folders _cwdSdkBaseDir = Path.Combine(_currentWorkingDir, "sdk"); - _userSdkBaseDir = Path.Combine(_userDir, ".dotnet", RepoDirectories.BuildArchitecture, "sdk"); _exeSdkBaseDir = Path.Combine(_exeDir, "sdk"); _regSdkBaseDir = Path.Combine(_regDir, "sdk"); // Create directories Directory.CreateDirectory(_cwdSdkBaseDir); - Directory.CreateDirectory(_userSdkBaseDir); Directory.CreateDirectory(_exeSdkBaseDir); Directory.CreateDirectory(_regSdkBaseDir); @@ -366,7 +361,6 @@ public void SdkMultilevelLookup_RegistryAccess() // Expected: 9999.0.4 from reg dir DotNet.Exec("help") .WorkingDirectory(_currentWorkingDir) - .WithUserProfile(_userDir) .MultilevelLookup(true) .ApplyRegisteredInstallLocationOverride(registeredInstallLocationOverride) .EnableTracingAndCaptureOutputs() @@ -409,7 +403,6 @@ public void SdkMultilevelLookup_Must_Pick_The_Highest_Semantic_Version() .And.HaveStdErrContaining(Path.Combine(_exeSelectedMessage, "9999.0.3", _dotnetSdkDllMessageTerminator)); // Add SDK versions - AddAvailableSdkVersions(_userSdkBaseDir, "9999.0.200"); AddAvailableSdkVersions(_cwdSdkBaseDir, "10000.0.0"); AddAvailableSdkVersions(_regSdkBaseDir, "9999.0.100"); @@ -562,12 +555,11 @@ CommandResult RunTest(string command, bool? multiLevelLookup = true) { return DotNet.Exec(command) .WorkingDirectory(_currentWorkingDir) - .WithUserProfile(_userDir) .MultilevelLookup(multiLevelLookup) .EnvironmentVariable(Constants.TestOnlyEnvironmentVariables.GloballyRegisteredPath, _regDir) .EnvironmentVariable( // Redirect the default install location to an invalid location so that a machine-wide install is not used Constants.TestOnlyEnvironmentVariables.DefaultInstallPath, - System.IO.Path.Combine(_userDir, "invalid")) + System.IO.Path.Combine(_currentWorkingDir, "invalid")) .EnableTracingAndCaptureOutputs() .Execute(); } diff --git a/src/installer/tests/HostActivation.Tests/SDKLookup.cs b/src/installer/tests/HostActivation.Tests/SDKLookup.cs index 7ed2b99d4af0d9..6cd0138dfbe05a 100644 --- a/src/installer/tests/HostActivation.Tests/SDKLookup.cs +++ b/src/installer/tests/HostActivation.Tests/SDKLookup.cs @@ -1040,7 +1040,6 @@ private CommandResult RunTest(string command) { return ExecutableDotNet.Exec(command) .WorkingDirectory(SharedState.CurrentWorkingDir) - .WithUserProfile(SharedState.UserDir) .EnableTracingAndCaptureOutputs() .MultilevelLookup(false) .Execute(); @@ -1056,8 +1055,6 @@ public class SharedTestState : IDisposable public string CurrentWorkingDir { get; } - public string UserDir { get; } - public string TestAssetsPath { get; } public SharedTestState() @@ -1081,11 +1078,6 @@ public SharedTestState() .Build(); CurrentWorkingDir = currentWorkingSdk.BinPath; - UserDir = Path.Combine(BaseDir, "user"); - var userSdk = new DotNetBuilder(Path.Combine(UserDir, ".dotnet", RepoDirectories.BuildArchitecture), BuiltDotNet.BinPath, null) - .AddMockSDK("9999.0.200", "9999.0.0") - .Build(); - TestAssetsPath = Path.Combine(RepoDirectories.TestAssetsFolder, "TestUtils", "SDKLookup"); } diff --git a/src/installer/tests/TestUtils/Command.cs b/src/installer/tests/TestUtils/Command.cs index a4e46f8dc475bd..726b2ed65da9ee 100644 --- a/src/installer/tests/TestUtils/Command.cs +++ b/src/installer/tests/TestUtils/Command.cs @@ -260,22 +260,6 @@ public Command WorkingDirectory(string projectDirectory) return this; } - public Command WithUserProfile(string userprofile) - { - string userDir; - if (OperatingSystem.IsWindows()) - { - userDir = "USERPROFILE"; - } - else - { - userDir = "HOME"; - } - - Process.StartInfo.Environment[userDir] = userprofile; - return this; - } - public Command EnvironmentVariable(string name, string value) { if (value == null) From 6c2ded0b135e69f5665cc48362ff0c74a7e9a75c Mon Sep 17 00:00:00 2001 From: Vitek Karas <10670590+vitek-karas@users.noreply.github.com> Date: Thu, 10 Mar 2022 03:09:53 -0800 Subject: [PATCH 3/4] Apply suggestions from code review Co-authored-by: Elinor Fung --- .../PerAssemblyVersionResolutionMultipleFrameworks.cs | 2 +- .../HostActivation.Tests/FrameworkResolution/MultipleHives.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/installer/tests/HostActivation.Tests/DependencyResolution/PerAssemblyVersionResolutionMultipleFrameworks.cs b/src/installer/tests/HostActivation.Tests/DependencyResolution/PerAssemblyVersionResolutionMultipleFrameworks.cs index 428fc60dbe8853..92ed54cabd4f75 100644 --- a/src/installer/tests/HostActivation.Tests/DependencyResolution/PerAssemblyVersionResolutionMultipleFrameworks.cs +++ b/src/installer/tests/HostActivation.Tests/DependencyResolution/PerAssemblyVersionResolutionMultipleFrameworks.cs @@ -64,7 +64,7 @@ public void AppWithSameAssemblyAsFramework(string testAssemblyName, string appAs [Theory] [InlineData("1.1.1")] // Exact match - no roll forward - [InlineData("1.1.0")] // Path roll forward + [InlineData("1.1.0")] // Patch roll forward [InlineData("1.0.0")] // Minor [InlineData("0.0.0")] // Major public void AppWithExactlySameAssemblyAsFrameworkWithRollForward(string frameworkReferenceVersion) diff --git a/src/installer/tests/HostActivation.Tests/FrameworkResolution/MultipleHives.cs b/src/installer/tests/HostActivation.Tests/FrameworkResolution/MultipleHives.cs index 014f98dd70d938..82479deb60c127 100644 --- a/src/installer/tests/HostActivation.Tests/FrameworkResolution/MultipleHives.cs +++ b/src/installer/tests/HostActivation.Tests/FrameworkResolution/MultipleHives.cs @@ -146,8 +146,8 @@ public void ListRuntimes(bool? multiLevelLookup) if (!OperatingSystem.IsWindows() && multiLevelLookup != false) return; - string expectedOutput = String.Join( - String.Empty, + string expectedOutput = string.Join( + string.Empty, GetExpectedFrameworks(multiLevelLookup) .Select(t => $"{MicrosoftNETCoreApp} {t.Version} [{Path.Combine(t.Path, "shared", MicrosoftNETCoreApp)}]{Environment.NewLine}")); From 15d81ffccb198d464c93985a3979ecc4819dfacd Mon Sep 17 00:00:00 2001 From: vitek-karas <10670590+vitek-karas@users.noreply.github.com> Date: Thu, 10 Mar 2022 03:28:15 -0800 Subject: [PATCH 4/4] PR Feedback --- .../FrameworkResolution/MultipleHives.cs | 24 ++++++--- .../MultilevelSDKLookup.cs | 50 +++++++------------ .../tests/HostActivation.Tests/SDKLookup.cs | 8 +-- .../HostActivation.Tests/StartupHooks.cs | 14 +++--- 4 files changed, 43 insertions(+), 53 deletions(-) diff --git a/src/installer/tests/HostActivation.Tests/FrameworkResolution/MultipleHives.cs b/src/installer/tests/HostActivation.Tests/FrameworkResolution/MultipleHives.cs index 82479deb60c127..1ae3df06f1631f 100644 --- a/src/installer/tests/HostActivation.Tests/FrameworkResolution/MultipleHives.cs +++ b/src/installer/tests/HostActivation.Tests/FrameworkResolution/MultipleHives.cs @@ -106,7 +106,7 @@ public void FxVersionCLI(string fxVersion, string tfm, bool? multiLevelLookup, s private record struct FrameworkInfo(string Name, string Version, int Level, string Path); - List GetExpectedFrameworks(bool? multiLevelLookup) + private List GetExpectedFrameworks(bool? multiLevelLookup) { // The runtimes should be ordered by version number List expectedList = new(); @@ -127,7 +127,13 @@ List GetExpectedFrameworks(bool? multiLevelLookup) if (result != 0) return result; - result = a.Version.CompareTo(b.Version); + if (!Version.TryParse(a.Version, out var aVersion)) + return -1; + + if (!Version.TryParse(b.Version, out var bVersion)) + return 1; + + result = aVersion.CompareTo(bVersion); if (result != 0) return result; @@ -151,8 +157,10 @@ public void ListRuntimes(bool? multiLevelLookup) GetExpectedFrameworks(multiLevelLookup) .Select(t => $"{MicrosoftNETCoreApp} {t.Version} [{Path.Combine(t.Path, "shared", MicrosoftNETCoreApp)}]{Environment.NewLine}")); - RunTest(new TestSettings() - .WithCommandLine("--list-runtimes"), + // !!IMPORTANT!!: This test verifies the exact match of the entire output of the command (not a substring!) + // This is important as the output of --list-runtimes is considered machine readable and thus must not change even in a minor way (unintentionally) + RunTest( + new TestSettings().WithCommandLine("--list-runtimes"), multiLevelLookup, testApp: null) .Should().HaveStdOut(expectedOutput); @@ -170,12 +178,12 @@ public void DotnetInfo(bool? multiLevelLookup) string expectedOutput = $".NET runtimes installed:{Environment.NewLine}" + - String.Join(String.Empty, + string.Join(string.Empty, GetExpectedFrameworks(multiLevelLookup) .Select(t => $" {MicrosoftNETCoreApp} {t.Version} [{Path.Combine(t.Path, "shared", MicrosoftNETCoreApp)}]{Environment.NewLine}")); - RunTest(new TestSettings() - .WithCommandLine("--info"), + RunTest( + new TestSettings().WithCommandLine("--info"), multiLevelLookup, testApp: null) .Should().HaveStdOutContaining(expectedOutput); @@ -196,7 +204,7 @@ public void FrameworkResolutionError(string tfm, bool? multiLevelLookup) string expectedOutput = $"The following frameworks were found:{Environment.NewLine}" + - String.Join(String.Empty, + string.Join(string.Empty, GetExpectedFrameworks(multiLevelLookup) .Select(t => $" {t.Version} at [{Path.Combine(t.Path, "shared", MicrosoftNETCoreApp)}]{Environment.NewLine}")); diff --git a/src/installer/tests/HostActivation.Tests/MultilevelSDKLookup.cs b/src/installer/tests/HostActivation.Tests/MultilevelSDKLookup.cs index 64ea450ddc8b61..96d7ca63d691ad 100644 --- a/src/installer/tests/HostActivation.Tests/MultilevelSDKLookup.cs +++ b/src/installer/tests/HostActivation.Tests/MultilevelSDKLookup.cs @@ -38,7 +38,7 @@ public MultilevelSDKLookup() string baseMultilevelDir = Path.Combine(TestArtifact.TestArtifactsPath, "dotnetMultilevelSDKLookup"); _multilevelDir = SharedFramework.CalculateUniqueTestDirectory(baseMultilevelDir); - // The tested locations will be the cwd, user folder, exe dir, and registered directory. cwd and user are no longer supported. + // The tested locations will be the cwd, exe dir, and registered directory. cwd is no longer supported. // All dirs will be placed inside the multilevel folder _currentWorkingDir = Path.Combine(_multilevelDir, "cwd"); _exeDir = Path.Combine(_multilevelDir, "exe"); @@ -86,7 +86,6 @@ public void SdkMultilevelLookup_Global_Json_Single_Digit_Patch_Rollup() // Specified SDK version: 9999.3.4-global-dummy // Cwd: empty - // User: empty // Exe: empty // Reg: empty // Expected: no compatible version and a specific error messages @@ -99,7 +98,6 @@ public void SdkMultilevelLookup_Global_Json_Single_Digit_Patch_Rollup() // Specified SDK version: 9999.3.4-global-dummy // Cwd: empty - // User: empty // Exe: 9999.4.1, 9999.3.4-dummy // Reg: empty // Expected: no compatible version and a specific error message @@ -112,7 +110,6 @@ public void SdkMultilevelLookup_Global_Json_Single_Digit_Patch_Rollup() // Specified SDK version: 9999.3.4-global-dummy // Cwd: empty - // User: empty // Exe: 9999.4.1, 9999.3.4-dummy // Reg: 9999.3.3 // Expected: no compatible version and a specific error message @@ -125,7 +122,6 @@ public void SdkMultilevelLookup_Global_Json_Single_Digit_Patch_Rollup() // Specified SDK version: 9999.3.4-global-dummy // Cwd: empty - // User: empty // Exe: 9999.4.1, 9999.3.4-dummy, 9999.3.4 // Reg: 9999.3.3 // Expected: 9999.3.4 from exe dir @@ -138,7 +134,6 @@ public void SdkMultilevelLookup_Global_Json_Single_Digit_Patch_Rollup() // Specified SDK version: 9999.3.4-global-dummy // Cwd: empty - // User: empty // Exe: 9999.4.1, 9999.3.4-dummy, 9999.3.4 // Reg: 9999.3.3, 9999.3.5-dummy // Expected: 9999.3.5-dummy from reg dir @@ -151,7 +146,6 @@ public void SdkMultilevelLookup_Global_Json_Single_Digit_Patch_Rollup() // Specified SDK version: 9999.3.4-global-dummy // Cwd: empty - // User: empty // Exe: 9999.4.1, 9999.3.4-dummy, 9999.3.4, 9999.3.600 // Reg: 9999.3.3, 9999.3.5-dummy // Expected: 9999.3.5-dummy from reg dir @@ -164,7 +158,6 @@ public void SdkMultilevelLookup_Global_Json_Single_Digit_Patch_Rollup() // Specified SDK version: 9999.3.4-global-dummy // Cwd: empty - // User: empty // Exe: 9999.4.1, 9999.3.4-dummy, 9999.3.4, 9999.3.600, 9999.3.4-global-dummy // Reg: 9999.3.3, 9999.3.5-dummy // Expected: 9999.3.4-global-dummy from exe dir @@ -193,7 +186,6 @@ public void SdkMultilevelLookup_Global_Json_Two_Part_Patch_Rollup() // Specified SDK version: 9999.3.304-global-dummy // Cwd: empty - // User: empty // Exe: empty // Reg: empty // Expected: no compatible version and a specific error messages @@ -206,7 +198,6 @@ public void SdkMultilevelLookup_Global_Json_Two_Part_Patch_Rollup() // Specified SDK version: 9999.3.304-global-dummy // Cwd: empty - // User: empty // Exe: empty // Reg: 9999.3.57, 9999.3.4-dummy // Expected: no compatible version and a specific error message @@ -219,7 +210,6 @@ public void SdkMultilevelLookup_Global_Json_Two_Part_Patch_Rollup() // Specified SDK version: 9999.3.304-global-dummy // Cwd: empty - // User: empty // Exe: 9999.3.300, 9999.7.304-global-dummy // Reg: 9999.3.57, 9999.3.4-dummy // Expected: no compatible version and a specific error message @@ -232,7 +222,6 @@ public void SdkMultilevelLookup_Global_Json_Two_Part_Patch_Rollup() // Specified SDK version: 9999.3.304-global-dummy // Cwd: empty - // User: empty // Exe: 9999.3.300, 9999.7.304-global-dummy // Reg: 9999.3.57, 9999.3.4-dummy, 9999.3.304 // Expected: 9999.3.304 from reg dir @@ -245,7 +234,6 @@ public void SdkMultilevelLookup_Global_Json_Two_Part_Patch_Rollup() // Specified SDK version: 9999.3.304-global-dummy // Cwd: empty - // User: empty // Exe: 9999.3.300, 9999.7.304-global-dummy, 9999.3.399, 9999.3.399-dummy, 9999.3.400 // Reg: 9999.3.57, 9999.3.4-dummy, 9999.3.304 // Expected: 9999.3.399 from exe dir @@ -259,7 +247,6 @@ public void SdkMultilevelLookup_Global_Json_Two_Part_Patch_Rollup() // Specified SDK version: 9999.3.304-global-dummy // Cwd: empty - // User: empty // Exe: 9999.3.300, 9999.7.304-global-dummy, 9999.3.399, 9999.3.399-dummy, 9999.3.400, 9999.3.2400, 9999.3.3004 // Reg: 9999.3.57, 9999.3.4-dummy, 9999.3.304, 9999.3.2400, 9999.3.3004 // Expected: 9999.3.399 from exe dir @@ -272,7 +259,6 @@ public void SdkMultilevelLookup_Global_Json_Two_Part_Patch_Rollup() // Specified SDK version: 9999.3.304-global-dummy // Cwd: empty - // User: empty // Exe: 9999.3.300, 9999.7.304-global-dummy, 9999.3.399, 9999.3.399-dummy, 9999.3.400, 9999.3.2400, 9999.3.3004 // Reg: 9999.3.57, 9999.3.4-dummy, 9999.3.304, 9999.3.2400, 9999.3.3004, 9999.3.304-global-dummy // Expected: 9999.3.304-global-dummy from reg dir @@ -307,7 +293,6 @@ public void SdkMultilevelLookup_Precedential_Order() // Specified SDK version: none // Cwd: empty - // User: empty // Exe: empty // Reg: 9999.0.4 // Expected: 9999.0.4 from reg dir @@ -320,7 +305,6 @@ public void SdkMultilevelLookup_Precedential_Order() // Specified SDK version: none // Cwd: empty - // User: empty // Exe: 9999.0.4 // Reg: 9999.0.4 // Expected: 9999.0.4 from exe dir @@ -355,7 +339,6 @@ public void SdkMultilevelLookup_RegistryAccess() // Specified SDK version: none // Cwd: empty - // User: empty // Exe: empty // Reg: 9999.0.4 // Expected: 9999.0.4 from reg dir @@ -381,7 +364,6 @@ public void SdkMultilevelLookup_Must_Pick_The_Highest_Semantic_Version() // Specified SDK version: none // Cwd: empty - // User: empty // Exe: empty // Reg: 9999.0.0, 9999.0.3-dummy // Expected: 9999.0.3-dummy from reg dir @@ -394,7 +376,6 @@ public void SdkMultilevelLookup_Must_Pick_The_Highest_Semantic_Version() // Specified SDK version: none // Cwd: empty - // User: empty // Exe: 9999.0.3 // Reg: 9999.0.0, 9999.0.3-dummy // Expected: 9999.0.3 from exe dir @@ -408,7 +389,6 @@ public void SdkMultilevelLookup_Must_Pick_The_Highest_Semantic_Version() // Specified SDK version: none // Cwd: 10000.0.0 --> should not be picked - // User: 9999.0.200 --> should not be picked // Exe: 9999.0.3 // Reg: 9999.0.0, 9999.0.3-dummy, 9999.0.100 // Expected: 9999.0.100 from reg dir @@ -421,7 +401,6 @@ public void SdkMultilevelLookup_Must_Pick_The_Highest_Semantic_Version() // Specified SDK version: none // Cwd: 10000.0.0 --> should not be picked - // User: 9999.0.200 --> should not be picked // Exe: 9999.0.3, 9999.0.80 // Reg: 9999.0.0, 9999.0.3-dummy, 9999.0.100 // Expected: 9999.0.100 from reg dir @@ -434,7 +413,6 @@ public void SdkMultilevelLookup_Must_Pick_The_Highest_Semantic_Version() // Specified SDK version: none // Cwd: 10000.0.0 --> should not be picked - // User: 9999.0.200 --> should not be picked // Exe: 9999.0.3, 9999.0.80, 9999.0.5500000 // Reg: 9999.0.0, 9999.0.3-dummy, 9999.0.100 // Expected: 9999.0.5500000 from exe dir @@ -447,7 +425,6 @@ public void SdkMultilevelLookup_Must_Pick_The_Highest_Semantic_Version() // Specified SDK version: none // Cwd: 10000.0.0 --> should not be picked - // User: 9999.0.200 --> should not be picked // Exe: 9999.0.3, 9999.0.80, 9999.0.5500000 // Reg: 9999.0.0, 9999.0.3-dummy, 9999.0.100, 9999.0.52000000 // Expected: 9999.0.52000000 from reg dir @@ -467,7 +444,7 @@ public void SdkMultilevelLookup_Must_Pick_The_Highest_Semantic_Version() .And.HaveStdOutContaining("9999.0.52000000"); } - List<(string version, string rootPath)> AddSdkVersionsAndGetExpectedList(bool? multiLevelLookup) + private List<(string version, string rootPath)> AddSdkVersionsAndGetExpectedList(bool? multiLevelLookup) { AddAvailableSdkVersions(_exeSdkBaseDir, "5.0.2"); AddAvailableSdkVersions(_exeSdkBaseDir, "6.1.1"); @@ -485,7 +462,16 @@ public void SdkMultilevelLookup_Must_Pick_The_Highest_Semantic_Version() expectedList.Add(("6.2.0", _regSdkBaseDir)); expectedList.Add(("7.0.1", _regSdkBaseDir)); } - expectedList.Sort((a, b) => a.version.CompareTo(b.version)); + expectedList.Sort((a, b) => + { + if (!Version.TryParse(a.version, out var aVersion)) + return -1; + + if (!Version.TryParse(b.version, out var bVersion)) + return 1; + + return aVersion.CompareTo(bVersion); + }); return expectedList; } @@ -500,8 +486,10 @@ public void ListSdks(bool? multiLevelLookup) return; var expectedList = AddSdkVersionsAndGetExpectedList(multiLevelLookup); - string expectedOutput = String.Join(String.Empty, expectedList.Select(t => $"{t.version} [{t.rootPath}]{Environment.NewLine}")); + string expectedOutput = string.Join(string.Empty, expectedList.Select(t => $"{t.version} [{t.rootPath}]{Environment.NewLine}")); + // !!IMPORTANT!!: This test verifies the exact match of the entire output of the command (not a substring!) + // This is important as the output of --list-sdks is considered machine readable and thus must not change even in a minor way (unintentionally) RunTest("--list-sdks", multiLevelLookup) .Should().Pass() .And.HaveStdOut(expectedOutput); @@ -522,7 +510,7 @@ public void SdkResolutionError(bool? multiLevelLookup) // When we fail to resolve SDK version, we print out all available SDKs var expectedList = AddSdkVersionsAndGetExpectedList(multiLevelLookup); - string expectedOutput = String.Join(String.Empty, expectedList.Select(t => $" {t.version} [{t.rootPath}]{Environment.NewLine}")); + string expectedOutput = string.Join(string.Empty, expectedList.Select(t => $" {t.version} [{t.rootPath}]{Environment.NewLine}")); RunTest("help", multiLevelLookup) .Should().Fail() @@ -542,16 +530,16 @@ public void DotnetInfo(bool? multiLevelLookup) var expectedList = AddSdkVersionsAndGetExpectedList(multiLevelLookup); string expectedOutput = $".NET SDKs installed:{Environment.NewLine}" + - String.Join(String.Empty, expectedList.Select(t => $" {t.version} [{t.rootPath}]{Environment.NewLine}")); + string.Join(string.Empty, expectedList.Select(t => $" {t.version} [{t.rootPath}]{Environment.NewLine}")); RunTest("--info", multiLevelLookup) .Should().Pass() .And.HaveStdOutContaining(expectedOutput); } - CommandResult RunTest() => RunTest("help"); + private CommandResult RunTest() => RunTest("help"); - CommandResult RunTest(string command, bool? multiLevelLookup = true) + private CommandResult RunTest(string command, bool? multiLevelLookup = true) { return DotNet.Exec(command) .WorkingDirectory(_currentWorkingDir) diff --git a/src/installer/tests/HostActivation.Tests/SDKLookup.cs b/src/installer/tests/HostActivation.Tests/SDKLookup.cs index 6cd0138dfbe05a..a8ec31b413cce0 100644 --- a/src/installer/tests/HostActivation.Tests/SDKLookup.cs +++ b/src/installer/tests/HostActivation.Tests/SDKLookup.cs @@ -259,7 +259,6 @@ public void SdkLookup_Must_Pick_The_Highest_Semantic_Version() // Specified SDK version: none // Cwd: 10000.0.0 --> should not be picked - // User: 9999.0.200 --> should not be picked // Exe: 9999.0.0, 9999.0.3-dummy.9, 9999.0.3-dummy.10 // Expected: 9999.0.3-dummy.10 from exe dir RunTest() @@ -271,7 +270,6 @@ public void SdkLookup_Must_Pick_The_Highest_Semantic_Version() // Specified SDK version: none // Cwd: 10000.0.0 --> should not be picked - // User: 9999.0.200 --> should not be picked // Exe: 9999.0.0, 9999.0.3-dummy.9, 9999.0.3-dummy.10, 9999.0.3 // Expected: 9999.0.3 from exe dir RunTest() @@ -283,7 +281,6 @@ public void SdkLookup_Must_Pick_The_Highest_Semantic_Version() // Specified SDK version: none // Cwd: 10000.0.0 --> should not be picked - // User: 9999.0.200 --> should not be picked // Exe: 9999.0.0, 9999.0.3-dummy.9, 9999.0.3-dummy.10, 9999.0.3, 9999.0.100 // Expected: 9999.0.100 from exe dir RunTest() @@ -295,7 +292,6 @@ public void SdkLookup_Must_Pick_The_Highest_Semantic_Version() // Specified SDK version: none // Cwd: 10000.0.0 --> should not be picked - // User: 9999.0.200 --> should not be picked // Exe: 9999.0.0, 9999.0.3-dummy.9, 9999.0.3-dummy.10, 9999.0.3, 9999.0.100, 9999.0.80 // Expected: 9999.0.100 from exe dir RunTest() @@ -307,7 +303,6 @@ public void SdkLookup_Must_Pick_The_Highest_Semantic_Version() // Specified SDK version: none // Cwd: 10000.0.0 --> should not be picked - // User: 9999.0.200 --> should not be picked // Exe: 9999.0.0, 9999.0.3-dummy.9, 9999.0.3-dummy.10, 9999.0.3, 9999.0.100, 9999.0.80, 9999.0.5500000 // Expected: 9999.0.5500000 from exe dir RunTest() @@ -319,7 +314,6 @@ public void SdkLookup_Must_Pick_The_Highest_Semantic_Version() // Specified SDK version: none // Cwd: 10000.0.0 --> should not be picked - // User: 9999.0.200 --> should not be picked // Exe: 9999.0.0, 9999.0.3-dummy.9, 9999.0.3-dummy.10, 9999.0.3, 9999.0.100, 9999.0.80, 9999.0.5500000, 9999.0.52000000 // Expected: 9999.0.52000000 from exe dir RunTest() @@ -1064,7 +1058,7 @@ public SharedTestState() string baseDir = Path.Combine(TestArtifact.TestArtifactsPath, "dotnetSDKLookup"); BaseDir = SharedFramework.CalculateUniqueTestDirectory(baseDir); - // The three tested locations will be the cwd, the user folder and the exe dir. cwd and user are no longer supported. + // The three tested locations will be the cwd and the exe dir. cwd is no longer supported. // All dirs will be placed inside the base folder BuiltDotNet = new DotNetCli(Path.Combine(TestArtifact.TestArtifactsPath, "sharedFrameworkPublish")); diff --git a/src/installer/tests/HostActivation.Tests/StartupHooks.cs b/src/installer/tests/HostActivation.Tests/StartupHooks.cs index 3e1685356167f9..00f209cc01b224 100644 --- a/src/installer/tests/HostActivation.Tests/StartupHooks.cs +++ b/src/installer/tests/HostActivation.Tests/StartupHooks.cs @@ -438,7 +438,7 @@ public void Muxer_activation_of_Missing_StartupHook_Assembly_Fails() .CaptureStdErr() .Execute(fExpectedToFail: true) .Should().Fail() - .And.HaveStdErrContaining(String.Format(expectedError, Path.GetFullPath(startupHookMissingDll))); + .And.HaveStdErrContaining(string.Format(expectedError, Path.GetFullPath(startupHookMissingDll))); // Missing dll is detected after previous hooks run startupHookVar = startupHookDll + Path.PathSeparator + startupHookMissingDll; @@ -449,7 +449,7 @@ public void Muxer_activation_of_Missing_StartupHook_Assembly_Fails() .Execute(fExpectedToFail: true) .Should().Fail() .And.HaveStdOutContaining("Hello from startup hook!") - .And.HaveStdErrContaining(String.Format(expectedError, Path.GetFullPath((startupHookMissingDll)))); + .And.HaveStdErrContaining(string.Format(expectedError, Path.GetFullPath((startupHookMissingDll)))); } // Run the app with an invalid startup hook assembly @@ -583,7 +583,7 @@ public void Muxer_activation_of_StartupHook_With_Incorrect_Method_Signature_Fail .CaptureStdErr() .Execute(fExpectedToFail: true) .Should().Fail() - .And.HaveStdErrContaining(String.Format(expectedError, startupHookWithInstanceMethodDll)); + .And.HaveStdErrContaining(string.Format(expectedError, startupHookWithInstanceMethodDll)); // Initialize method takes parameters var startupHookWithParameterFixture = sharedTestState.StartupHookWithParameterFixture.Copy(); @@ -594,7 +594,7 @@ public void Muxer_activation_of_StartupHook_With_Incorrect_Method_Signature_Fail .CaptureStdErr() .Execute(fExpectedToFail: true) .Should().Fail() - .And.HaveStdErrContaining(String.Format(expectedError, startupHookWithParameterDll)); + .And.HaveStdErrContaining(string.Format(expectedError, startupHookWithParameterDll)); // Initialize method has non-void return type var startupHookWithReturnTypeFixture = sharedTestState.StartupHookWithReturnTypeFixture.Copy(); @@ -605,7 +605,7 @@ public void Muxer_activation_of_StartupHook_With_Incorrect_Method_Signature_Fail .CaptureStdErr() .Execute(fExpectedToFail: true) .Should().Fail() - .And.HaveStdErrContaining(String.Format(expectedError, startupHookWithReturnTypeDll)); + .And.HaveStdErrContaining(string.Format(expectedError, startupHookWithReturnTypeDll)); // Initialize method that has multiple methods with an incorrect signature var startupHookWithMultipleIncorrectSignaturesFixture = sharedTestState.StartupHookWithMultipleIncorrectSignaturesFixture.Copy(); @@ -616,7 +616,7 @@ public void Muxer_activation_of_StartupHook_With_Incorrect_Method_Signature_Fail .CaptureStdErr() .Execute(fExpectedToFail: true) .Should().Fail() - .And.HaveStdErrContaining(String.Format(expectedError, startupHookWithMultipleIncorrectSignaturesDll)); + .And.HaveStdErrContaining(string.Format(expectedError, startupHookWithMultipleIncorrectSignaturesDll)); // Signature problem is caught after previous hooks have run var startupHookVar = startupHookDll + Path.PathSeparator + startupHookWithMultipleIncorrectSignaturesDll; @@ -627,7 +627,7 @@ public void Muxer_activation_of_StartupHook_With_Incorrect_Method_Signature_Fail .Execute(fExpectedToFail: true) .Should().Fail() .And.HaveStdOutContaining("Hello from startup hook!") - .And.HaveStdErrContaining(String.Format(expectedError, startupHookWithMultipleIncorrectSignaturesDll)); + .And.HaveStdErrContaining(string.Format(expectedError, startupHookWithMultipleIncorrectSignaturesDll)); } private static void RemoveLibraryFromDepsJson(string depsJsonPath, string libraryName)