From 7abdc5a66e0f6fa1c44055a14732a551a238c011 Mon Sep 17 00:00:00 2001 From: Elinor Fung Date: Tue, 2 Apr 2024 19:13:30 -0700 Subject: [PATCH] Cleanup of `fx_resolver_t` and tests in `NativeHostApis` (#100542) Slight cleanup of `fx_resolver_t` and `NativeHostApis` tests in preparation for #99027: - Collapse `reconcile_fx_references_helper` into `reconcile_fx_references` - Make `NativeHostApis` tests / `HostApiInvokerApp` more consistent in how they log and validate results --- .../tests/AppHost.Bundle.Tests/BundleProbe.cs | 3 +- .../Projects/HostApiInvokerApp/HostFXR.cs | 76 +++---- .../HostApiInvokerApp/HostRuntimeContract.cs | 4 +- .../Projects/HostApiInvokerApp/Program.cs | 9 +- .../HostActivation.Tests/NativeHostApis.cs | 208 ++++++++---------- .../Assertions/CommandResultAssertions.cs | 4 +- src/installer/tests/TestUtils/Constants.cs | 1 + src/native/corehost/fxr/fx_resolver.cpp | 84 +++---- src/native/corehost/fxr/fx_resolver.h | 6 +- 9 files changed, 165 insertions(+), 230 deletions(-) diff --git a/src/installer/tests/AppHost.Bundle.Tests/BundleProbe.cs b/src/installer/tests/AppHost.Bundle.Tests/BundleProbe.cs index c74a9d211eea9..6c7fc9f3f43e9 100644 --- a/src/installer/tests/AppHost.Bundle.Tests/BundleProbe.cs +++ b/src/installer/tests/AppHost.Bundle.Tests/BundleProbe.cs @@ -35,8 +35,7 @@ private void SingleFileApp_ProbeFiles() }; var result = Command.Create(singleFile, $"host_runtime_contract.bundle_probe {string.Join(" ", itemsToProbe.Select(i => i.Path))}") - .CaptureStdErr() - .CaptureStdOut() + .EnableTracingAndCaptureOutputs() .Execute(); result.Should().Pass(); diff --git a/src/installer/tests/Assets/Projects/HostApiInvokerApp/HostFXR.cs b/src/installer/tests/Assets/Projects/HostApiInvokerApp/HostFXR.cs index 76dc6db1d65f2..3dc91d5ea69f7 100644 --- a/src/installer/tests/Assets/Projects/HostApiInvokerApp/HostFXR.cs +++ b/src/installer/tests/Assets/Projects/HostApiInvokerApp/HostFXR.cs @@ -106,62 +106,48 @@ internal static extern int hostfxr_get_dotnet_environment_info( /// /// Test invoking the native hostfxr api hostfxr_resolve_sdk2 /// - /// hostfxr_get_available_sdks - /// Directory of dotnet executable - /// Working directory where search for global.json begins - /// Flags + /// Directory of dotnet executable + /// Working directory where search for global.json begins + /// Flags static void Test_hostfxr_resolve_sdk2(string[] args) { - if (args.Length != 4) + if (args.Length != 3) { throw new ArgumentException("Invalid number of arguments passed"); } var data = new List<(hostfxr.hostfxr_resolve_sdk2_result_key_t, string)>(); int rc = hostfxr.hostfxr_resolve_sdk2( - exe_dir: args[1], - working_dir: args[2], - flags: Enum.Parse(args[3]), + exe_dir: args[0], + working_dir: args[1], + flags: Enum.Parse(args[2]), result: (key, value) => data.Add((key, value))); - if (rc == 0) - { - Console.WriteLine("hostfxr_resolve_sdk2:Success"); - } - else - { - Console.WriteLine($"hostfxr_resolve_sdk2:Fail[{rc}]"); - } - - Console.WriteLine($"hostfxr_resolve_sdk2 data:[{string.Join(';', data)}]"); + string api = nameof(hostfxr.hostfxr_resolve_sdk2); + LogResult(api, rc); + Console.WriteLine($"{api} data:[{string.Join(';', data)}]"); } /// /// Test invoking the native hostfxr api hostfxr_get_available_sdks /// - /// hostfxr_get_available_sdks - /// Directory of dotnet executable + /// Directory of dotnet executable static void Test_hostfxr_get_available_sdks(string[] args) { - if (args.Length != 2) + if (args.Length != 1) { throw new ArgumentException("Invalid number of arguments passed"); } string[] sdks = null; int rc = hostfxr.hostfxr_get_available_sdks( - exe_dir: args[1], + exe_dir: args[0], (sdk_count, sdk_dirs) => sdks = sdk_dirs); - if (rc == 0) - { - Console.WriteLine("hostfxr_get_available_sdks:Success"); - Console.WriteLine($"hostfxr_get_available_sdks sdks:[{string.Join(';', sdks)}]"); - } - else - { - Console.WriteLine($"hostfxr_get_available_sdks:Fail[{rc}]"); - } + string api = nameof(hostfxr.hostfxr_get_available_sdks); + LogResult(api, rc); + if (sdks != null) + Console.WriteLine($"{api} sdks:[{string.Join(';', sdks)}]"); } static void Test_hostfxr_set_error_writer(string[] args) @@ -193,13 +179,12 @@ static void Test_hostfxr_set_error_writer(string[] args) /// /// Test that invokes native api hostfxr_get_dotnet_environment_info. /// - /// hostfxr_get_dotnet_environment_info - /// (Optional) Path to the directory with dotnet.exe + /// (Optional) Path to the directory with dotnet.exe static void Test_hostfxr_get_dotnet_environment_info(string[] args) { string dotnetExeDir = null; - if (args.Length >= 2) - dotnetExeDir = args[1]; + if (args.Length >= 1) + dotnetExeDir = args[0]; string hostfxr_version; string hostfxr_commit_hash; @@ -254,21 +239,20 @@ static void Test_hostfxr_get_dotnet_environment_info(string[] args) result: result_fn, result_context: new IntPtr(42)); - if (rc != 0) - { - Console.WriteLine($"hostfxr_get_dotnet_environment_info:Fail[{rc}]"); - } - - Console.WriteLine($"hostfxr_get_dotnet_environment_info sdk versions:[{string.Join(";", sdks.Select(s => s.version).ToList())}]"); - Console.WriteLine($"hostfxr_get_dotnet_environment_info sdk paths:[{string.Join(";", sdks.Select(s => s.path).ToList())}]"); + string api = nameof(hostfxr.hostfxr_get_dotnet_environment_info); + LogResult(api, rc); - Console.WriteLine($"hostfxr_get_dotnet_environment_info framework names:[{string.Join(";", frameworks.Select(f => f.name).ToList())}]"); - Console.WriteLine($"hostfxr_get_dotnet_environment_info framework versions:[{string.Join(";", frameworks.Select(f => f.version).ToList())}]"); - Console.WriteLine($"hostfxr_get_dotnet_environment_info framework paths:[{string.Join(";", frameworks.Select(f => f.path).ToList())}]"); + Console.WriteLine($"{api} sdk versions:[{string.Join(";", sdks.Select(s => s.version).ToList())}]"); + Console.WriteLine($"{api} sdk paths:[{string.Join(";", sdks.Select(s => s.path).ToList())}]"); - Console.WriteLine("hostfxr_get_dotnet_environment_info:Success"); + Console.WriteLine($"{api} framework names:[{string.Join(";", frameworks.Select(f => f.name).ToList())}]"); + Console.WriteLine($"{api} framework versions:[{string.Join(";", frameworks.Select(f => f.version).ToList())}]"); + Console.WriteLine($"{api} framework paths:[{string.Join(";", frameworks.Select(f => f.path).ToList())}]"); } + private static void LogResult(string apiName, int rc) + => Console.WriteLine(rc == 0 ? $"{apiName}:Success" : $"{apiName}:Fail[0x{rc:x}]"); + public static bool RunTest(string apiToTest, string[] args) { switch (apiToTest) diff --git a/src/installer/tests/Assets/Projects/HostApiInvokerApp/HostRuntimeContract.cs b/src/installer/tests/Assets/Projects/HostApiInvokerApp/HostRuntimeContract.cs index 99ebe3adfe5d0..4ecf59b2761ac 100644 --- a/src/installer/tests/Assets/Projects/HostApiInvokerApp/HostRuntimeContract.cs +++ b/src/installer/tests/Assets/Projects/HostApiInvokerApp/HostRuntimeContract.cs @@ -113,10 +113,10 @@ public static bool RunTest(string apiToTest, string[] args) switch (apiToTest) { case $"{nameof(host_runtime_contract)}.{nameof(host_runtime_contract.get_runtime_property)}": - Test_get_runtime_property(args[1..]); + Test_get_runtime_property(args); break; case $"{nameof(host_runtime_contract)}.{nameof(host_runtime_contract.bundle_probe)}": - Test_bundle_probe(args[1..]); + Test_bundle_probe(args); break; default: return false; diff --git a/src/installer/tests/Assets/Projects/HostApiInvokerApp/Program.cs b/src/installer/tests/Assets/Projects/HostApiInvokerApp/Program.cs index 2831ed8c3d487..f14003995e6a2 100644 --- a/src/installer/tests/Assets/Projects/HostApiInvokerApp/Program.cs +++ b/src/installer/tests/Assets/Projects/HostApiInvokerApp/Program.cs @@ -31,9 +31,6 @@ public static void MainCore(string[] args) Console.WriteLine("Hello World!"); Console.WriteLine(string.Join(Environment.NewLine, args)); - // Enable tracing so that test assertion failures are easier to diagnose. - Environment.SetEnvironmentVariable("COREHOST_TRACE", "1"); - // If requested, test multilevel lookup using fake Global SDK directories: // 1. using a fake ProgramFiles location // 2. using a fake SDK Self-Registered location @@ -61,13 +58,13 @@ public static void MainCore(string[] args) } string apiToTest = args[0]; - if (HostFXR.RunTest(apiToTest, args)) + if (HostFXR.RunTest(apiToTest, args[1..])) return; - if (HostPolicy.RunTest(apiToTest, args)) + if (HostPolicy.RunTest(apiToTest, args[1..])) return; - if (HostRuntimeContract.RunTest(apiToTest, args)) + if (HostRuntimeContract.RunTest(apiToTest, args[1..])) return; throw new ArgumentException($"Invalid API to test passed as args[0]): {apiToTest}"); diff --git a/src/installer/tests/HostActivation.Tests/NativeHostApis.cs b/src/installer/tests/HostActivation.Tests/NativeHostApis.cs index 959239d08d030..0c1ba3116d49a 100644 --- a/src/installer/tests/HostActivation.Tests/NativeHostApis.cs +++ b/src/installer/tests/HostActivation.Tests/NativeHostApis.cs @@ -4,7 +4,7 @@ using System; using System.Collections.Generic; using System.IO; - +using FluentAssertions; using Microsoft.DotNet.Cli.Build; using Microsoft.DotNet.TestUtils; using Xunit; @@ -20,6 +20,13 @@ public NativeHostApis(SharedTestState fixture) sharedTestState = fixture; } + private class ApiNames + { + public const string hostfxr_get_available_sdks = nameof(hostfxr_get_available_sdks); + public const string hostfxr_resolve_sdk2 = nameof(hostfxr_resolve_sdk2); + public const string hostfxr_get_dotnet_environment_info = nameof(hostfxr_get_dotnet_environment_info); + } + private class SdkResolutionFixture { private readonly TestApp _app; @@ -124,17 +131,17 @@ public void Hostfxr_get_available_sdks_with_multilevel_lookup() Path.Combine(f.LocalSdkDir, "5.6.7-preview"), }); + string api = ApiNames.hostfxr_get_available_sdks; using (TestOnlyProductBehavior.Enable(f.Dotnet.GreatestVersionHostFxrFilePath)) { - f.Dotnet.Exec(f.AppDll, new[] { "hostfxr_get_available_sdks", f.ExeDir }) + f.Dotnet.Exec(f.AppDll, api, f.ExeDir) .EnvironmentVariable("TEST_MULTILEVEL_LOOKUP_PROGRAM_FILES", f.ProgramFiles) .EnvironmentVariable("TEST_MULTILEVEL_LOOKUP_SELF_REGISTERED", f.SelfRegistered) - .CaptureStdOut() - .CaptureStdErr() + .EnableTracingAndCaptureOutputs() .Execute() .Should().Pass() - .And.HaveStdOutContaining("hostfxr_get_available_sdks:Success") - .And.HaveStdOutContaining($"hostfxr_get_available_sdks sdks:[{expectedList}]"); + .And.ReturnStatusCode(api, Constants.ErrorCode.Success) + .And.HaveStdOutContaining($"{api} sdks:[{expectedList}]"); } } @@ -152,13 +159,13 @@ public void Hostfxr_get_available_sdks_without_multilevel_lookup() Path.Combine(f.LocalSdkDir, "5.6.7-preview"), }); - f.Dotnet.Exec(f.AppDll, new[] { "hostfxr_get_available_sdks", f.ExeDir }) - .CaptureStdOut() - .CaptureStdErr() + string api = ApiNames.hostfxr_get_available_sdks; + f.Dotnet.Exec(f.AppDll, api, f.ExeDir) + .EnableTracingAndCaptureOutputs() .Execute() .Should().Pass() - .And.HaveStdOutContaining("hostfxr_get_available_sdks:Success") - .And.HaveStdOutContaining($"hostfxr_get_available_sdks sdks:[{expectedList}]"); + .And.ReturnStatusCode(api, Constants.ErrorCode.Success) + .And.HaveStdOutContaining($"{api} sdks:[{expectedList}]"); } [Fact] @@ -173,13 +180,13 @@ public void Hostfxr_resolve_sdk2_without_global_json_or_flags() ("resolved_sdk_dir", Path.Combine(f.LocalSdkDir, "5.6.7-preview")), }); - f.Dotnet.Exec(f.AppDll, new[] { "hostfxr_resolve_sdk2", f.ExeDir, f.WorkingDir, "0" }) - .CaptureStdOut() - .CaptureStdErr() + string api = ApiNames.hostfxr_resolve_sdk2; + f.Dotnet.Exec(f.AppDll, api, f.ExeDir, f.WorkingDir, "0") + .EnableTracingAndCaptureOutputs() .Execute() .Should().Pass() - .And.HaveStdOutContaining("hostfxr_resolve_sdk2:Success") - .And.HaveStdOutContaining($"hostfxr_resolve_sdk2 data:[{expectedData}]"); + .And.ReturnStatusCode(api, Constants.ErrorCode.Success) + .And.HaveStdOutContaining($"{api} data:[{expectedData}]"); } [Fact] @@ -194,13 +201,13 @@ public void Hostfxr_resolve_sdk2_without_global_json_and_disallowing_previews() ("resolved_sdk_dir", Path.Combine(f.LocalSdkDir, "1.2.3")) }); - f.Dotnet.Exec(f.AppDll, new[] { "hostfxr_resolve_sdk2", f.ExeDir, f.WorkingDir, "disallow_prerelease" }) - .CaptureStdOut() - .CaptureStdErr() + string api = ApiNames.hostfxr_resolve_sdk2; + f.Dotnet.Exec(f.AppDll, api, f.ExeDir, f.WorkingDir, "disallow_prerelease") + .EnableTracingAndCaptureOutputs() .Execute() .Should().Pass() - .And.HaveStdOutContaining("hostfxr_resolve_sdk2:Success") - .And.HaveStdOutContaining($"hostfxr_resolve_sdk2 data:[{expectedData}]"); + .And.ReturnStatusCode(api, Constants.ErrorCode.Success) + .And.HaveStdOutContaining($"{api} data:[{expectedData}]"); } [Fact] @@ -221,21 +228,20 @@ public void Hostfxr_resolve_sdk2_with_global_json_and_disallowing_previews() ("requested_version", requestedVersion), }); - f.Dotnet.Exec(f.AppDll, new[] { "hostfxr_resolve_sdk2", f.ExeDir, f.WorkingDir, "disallow_prerelease" }) - .CaptureStdOut() - .CaptureStdErr() + string api = ApiNames.hostfxr_resolve_sdk2; + f.Dotnet.Exec(f.AppDll, api, f.ExeDir, f.WorkingDir, "disallow_prerelease") + .EnableTracingAndCaptureOutputs() .Execute() .Should().Pass() - .And.HaveStdOutContaining("hostfxr_resolve_sdk2:Success") - .And.HaveStdOutContaining($"hostfxr_resolve_sdk2 data:[{expectedData}]"); + .And.ReturnStatusCode(api, Constants.ErrorCode.Success) + .And.HaveStdOutContaining($"{api} data:[{expectedData}]"); } [Fact] public void Hostfxr_corehost_set_error_writer_test() { TestContext.BuiltDotNet.Exec(sharedTestState.HostApiInvokerApp.AppDll, "Test_hostfxr_set_error_writer") - .CaptureStdOut() - .CaptureStdErr() + .EnableTracingAndCaptureOutputs() .Execute() .Should().Pass(); } @@ -279,17 +285,17 @@ public void Hostfxr_get_dotnet_environment_info_dotnet_root_only() Path.Combine(f.LocalFrameworksDir, "HostFxr.Test.C") }); - f.Dotnet.Exec(f.AppDll, new[] { "hostfxr_get_dotnet_environment_info", f.ExeDir }) - .CaptureStdOut() - .CaptureStdErr() - .Execute() - .Should().Pass() - .And.HaveStdOutContaining("hostfxr_get_dotnet_environment_info:Success") - .And.HaveStdOutContaining($"hostfxr_get_dotnet_environment_info sdk versions:[{expectedSdkVersions}]") - .And.HaveStdOutContaining($"hostfxr_get_dotnet_environment_info sdk paths:[{expectedSdkPaths}]") - .And.HaveStdOutContaining($"hostfxr_get_dotnet_environment_info framework names:[{expectedFrameworkNames}]") - .And.HaveStdOutContaining($"hostfxr_get_dotnet_environment_info framework versions:[{expectedFrameworkVersions}]") - .And.HaveStdOutContaining($"hostfxr_get_dotnet_environment_info framework paths:[{expectedFrameworkPaths}]"); + string api = ApiNames.hostfxr_get_dotnet_environment_info; + f.Dotnet.Exec(f.AppDll, api, f.ExeDir) + .EnableTracingAndCaptureOutputs() + .Execute() + .Should().Pass() + .And.ReturnStatusCode(api, Constants.ErrorCode.Success) + .And.HaveStdOutContaining($"{api} sdk versions:[{expectedSdkVersions}]") + .And.HaveStdOutContaining($"{api} sdk paths:[{expectedSdkPaths}]") + .And.HaveStdOutContaining($"{api} framework names:[{expectedFrameworkNames}]") + .And.HaveStdOutContaining($"{api} framework versions:[{expectedFrameworkVersions}]") + .And.HaveStdOutContaining($"{api} framework paths:[{expectedFrameworkPaths}]"); } [Fact] @@ -334,19 +340,19 @@ public void Hostfxr_get_dotnet_environment_info_with_multilevel_lookup_with_dotn using (TestOnlyProductBehavior.Enable(f.Dotnet.GreatestVersionHostFxrFilePath)) { - f.Dotnet.Exec(f.AppDll, new[] { "hostfxr_get_dotnet_environment_info", f.ExeDir }) - .EnvironmentVariable("TEST_MULTILEVEL_LOOKUP_PROGRAM_FILES", f.ProgramFiles) - .EnvironmentVariable("TEST_MULTILEVEL_LOOKUP_SELF_REGISTERED", f.SelfRegistered) - .CaptureStdOut() - .CaptureStdErr() - .Execute() - .Should().Pass() - .And.HaveStdOutContaining("hostfxr_get_dotnet_environment_info:Success") - .And.HaveStdOutContaining($"hostfxr_get_dotnet_environment_info sdk versions:[{expectedSdkVersions}]") - .And.HaveStdOutContaining($"hostfxr_get_dotnet_environment_info sdk paths:[{expectedSdkPaths}]") - .And.HaveStdOutContaining($"hostfxr_get_dotnet_environment_info framework names:[{expectedFrameworkNames}]") - .And.HaveStdOutContaining($"hostfxr_get_dotnet_environment_info framework versions:[{expectedFrameworkVersions}]") - .And.HaveStdOutContaining($"hostfxr_get_dotnet_environment_info framework paths:[{expectedFrameworkPaths}]"); + string api = ApiNames.hostfxr_get_dotnet_environment_info; + f.Dotnet.Exec(f.AppDll, new[] { api, f.ExeDir }) + .EnvironmentVariable("TEST_MULTILEVEL_LOOKUP_PROGRAM_FILES", f.ProgramFiles) + .EnvironmentVariable("TEST_MULTILEVEL_LOOKUP_SELF_REGISTERED", f.SelfRegistered) + .EnableTracingAndCaptureOutputs() + .Execute() + .Should().Pass() + .And.ReturnStatusCode(api, Constants.ErrorCode.Success) + .And.HaveStdOutContaining($"{api} sdk versions:[{expectedSdkVersions}]") + .And.HaveStdOutContaining($"{api} sdk paths:[{expectedSdkPaths}]") + .And.HaveStdOutContaining($"{api} framework names:[{expectedFrameworkNames}]") + .And.HaveStdOutContaining($"{api} framework versions:[{expectedFrameworkVersions}]") + .And.HaveStdOutContaining($"{api} framework paths:[{expectedFrameworkPaths}]"); } } @@ -362,94 +368,66 @@ public void Hostfxr_get_dotnet_environment_info_with_multilevel_lookup_only() { // We pass f.WorkingDir so that we don't resolve dotnet_dir to the global installation // in the native side. - f.Dotnet.Exec(f.AppDll, new[] { "hostfxr_get_dotnet_environment_info", f.WorkingDir }) - .EnvironmentVariable("TEST_MULTILEVEL_LOOKUP_PROGRAM_FILES", f.ProgramFiles) - .EnvironmentVariable("TEST_MULTILEVEL_LOOKUP_SELF_REGISTERED", f.SelfRegistered) - .CaptureStdOut() - .CaptureStdErr() - .Execute() - .Should().Pass() - .And.HaveStdOutContaining("hostfxr_get_dotnet_environment_info:Success") - .And.HaveStdOutContaining($"hostfxr_get_dotnet_environment_info sdk versions:[]") - .And.HaveStdOutContaining($"hostfxr_get_dotnet_environment_info sdk paths:[]") - .And.HaveStdOutContaining($"hostfxr_get_dotnet_environment_info framework names:[]") - .And.HaveStdOutContaining($"hostfxr_get_dotnet_environment_info framework versions:[]") - .And.HaveStdOutContaining($"hostfxr_get_dotnet_environment_info framework paths:[]"); + string api = ApiNames.hostfxr_get_dotnet_environment_info; + f.Dotnet.Exec(f.AppDll, api, f.WorkingDir) + .EnvironmentVariable("TEST_MULTILEVEL_LOOKUP_PROGRAM_FILES", f.ProgramFiles) + .EnvironmentVariable("TEST_MULTILEVEL_LOOKUP_SELF_REGISTERED", f.SelfRegistered) + .EnableTracingAndCaptureOutputs() + .Execute() + .Should().Pass() + .And.ReturnStatusCode(api, Constants.ErrorCode.Success) + .And.HaveStdOutContaining($"{api} sdk versions:[]") + .And.HaveStdOutContaining($"{api} sdk paths:[]") + .And.HaveStdOutContaining($"{api} framework names:[]") + .And.HaveStdOutContaining($"{api} framework versions:[]") + .And.HaveStdOutContaining($"{api} framework paths:[]"); } } [Fact] - [PlatformSpecific(TestPlatforms.Windows)] // The test setup only works on Windows (and MLL was Windows-only anyway) - public void Hostfxr_get_dotnet_environment_info_with_multilevel_lookup_only_self_register_program_files() + public void Hostfxr_get_dotnet_environment_info_global_install_path() { + string api = ApiNames.hostfxr_get_dotnet_environment_info; var f = new SdkResolutionFixture(sharedTestState); - - using (TestOnlyProductBehavior.Enable(f.Dotnet.GreatestVersionHostFxrFilePath)) - { - // We pass f.WorkingDir so that we don't resolve dotnet_dir to the global installation - // in the native side. - f.Dotnet.Exec(f.AppDll, new[] { "hostfxr_get_dotnet_environment_info", f.WorkingDir }) - .EnvironmentVariable("TEST_MULTILEVEL_LOOKUP_PROGRAM_FILES", f.ProgramFiles) - // Test with a self-registered path the same as ProgramFiles, with a trailing slash. Expect this to be de-duped - .EnvironmentVariable("TEST_MULTILEVEL_LOOKUP_SELF_REGISTERED", Path.Combine(f.ProgramFiles, "dotnet") + Path.DirectorySeparatorChar) - .CaptureStdOut() - .CaptureStdErr() + f.Dotnet.Exec(f.AppDll, api) + .EnableTracingAndCaptureOutputs() .Execute() .Should().Pass() - .And.HaveStdOutContaining("hostfxr_get_dotnet_environment_info:Success") - .And.HaveStdOutContaining($"hostfxr_get_dotnet_environment_info framework names:[]") - .And.HaveStdOutContaining($"hostfxr_get_dotnet_environment_info framework versions:[]") - .And.HaveStdOutContaining($"hostfxr_get_dotnet_environment_info framework paths:[]"); - } - } - - [Fact] - public void Hostfxr_get_dotnet_environment_info_global_install_path() - { - var f = new SdkResolutionFixture(sharedTestState); - - f.Dotnet.Exec(f.AppDll, new[] { "hostfxr_get_dotnet_environment_info" }) - .CaptureStdOut() - .CaptureStdErr() - .Execute() - .Should().Pass() - .And.HaveStdOutContaining("hostfxr_get_dotnet_environment_info:Success"); + .And.ReturnStatusCode(api, Constants.ErrorCode.Success); } [Fact] public void Hostfxr_get_dotnet_environment_info_result_is_nullptr_fails() { var f = new SdkResolutionFixture(sharedTestState); - - f.Dotnet.Exec(f.AppDll, new[] { "hostfxr_get_dotnet_environment_info", "test_invalid_result_ptr" }) + string api = ApiNames.hostfxr_get_dotnet_environment_info; + f.Dotnet.Exec(f.AppDll, api, "test_invalid_result_ptr") .EnableTracingAndCaptureOutputs() .Execute() .Should().Pass() - // 0x80008081 (InvalidArgFailure) - .And.HaveStdOutContaining("hostfxr_get_dotnet_environment_info:Fail[-2147450751]") - .And.HaveStdErrContaining("hostfxr_get_dotnet_environment_info received an invalid argument: result should not be null."); + .And.ReturnStatusCode(api, Constants.ErrorCode.InvalidArgFailure) + .And.HaveStdErrContaining($"{api} received an invalid argument: result should not be null."); } [Fact] public void Hostfxr_get_dotnet_environment_info_reserved_is_not_nullptr_fails() { var f = new SdkResolutionFixture(sharedTestState); - - f.Dotnet.Exec(f.AppDll, new[] { "hostfxr_get_dotnet_environment_info", "test_invalid_reserved_ptr" }) + string api = ApiNames.hostfxr_get_dotnet_environment_info; + f.Dotnet.Exec(f.AppDll, api, "test_invalid_reserved_ptr") .EnableTracingAndCaptureOutputs() .Execute() .Should().Pass() // 0x80008081 (InvalidArgFailure) - .And.HaveStdOutContaining("hostfxr_get_dotnet_environment_info:Fail[-2147450751]") - .And.HaveStdErrContaining("hostfxr_get_dotnet_environment_info received an invalid argument: reserved should be null."); + .And.ReturnStatusCode(api, Constants.ErrorCode.InvalidArgFailure) + .And.HaveStdErrContaining($"{api} received an invalid argument: reserved should be null."); } [Fact] public void Hostpolicy_corehost_set_error_writer_test() { TestContext.BuiltDotNet.Exec(sharedTestState.HostApiInvokerApp.AppDll, "Test_corehost_set_error_writer") - .CaptureStdOut() - .CaptureStdErr() + .EnableTracingAndCaptureOutputs() .Execute() .Should().Pass(); } @@ -459,8 +437,7 @@ public void HostRuntimeContract_get_runtime_property() { TestApp app = sharedTestState.HostApiInvokerApp; TestContext.BuiltDotNet.Exec(app.AppDll, "host_runtime_contract.get_runtime_property", "APP_CONTEXT_BASE_DIRECTORY", "RUNTIME_IDENTIFIER", "DOES_NOT_EXIST", "ENTRY_ASSEMBLY_NAME") - .CaptureStdOut() - .CaptureStdErr() + .EnableTracingAndCaptureOutputs() .Execute() .Should().Pass() .And.HaveStdOutContaining($"APP_CONTEXT_BASE_DIRECTORY = {Path.GetDirectoryName(app.AppDll)}") @@ -473,8 +450,7 @@ public void HostRuntimeContract_get_runtime_property() public void HostRuntimeContract_bundle_probe() { TestContext.BuiltDotNet.Exec(sharedTestState.HostApiInvokerApp.AppDll, "host_runtime_contract.bundle_probe", "APP_CONTEXT_BASE_DIRECTORY", "RUNTIME_IDENTIFIER", "DOES_NOT_EXIST", "ENTRY_ASSEMBLY_NAME") - .CaptureStdOut() - .CaptureStdErr() + .EnableTracingAndCaptureOutputs() .Execute() .Should().Pass() .And.HaveStdOutContaining("host_runtime_contract.bundle_probe is not set"); @@ -501,4 +477,14 @@ public void Dispose() } } } + + public static class HostApisCommandResultExtensions + { + public static AndConstraint ReturnStatusCode(this CommandResultAssertions assertion, string apiName, int statusCode) + { + return statusCode == Constants.ErrorCode.Success + ? assertion.HaveStdOutContaining($"{apiName}:Success") + : assertion.HaveStdOutContaining($"{apiName}:Fail[0x{statusCode:x}]"); + } + } } diff --git a/src/installer/tests/TestUtils/Assertions/CommandResultAssertions.cs b/src/installer/tests/TestUtils/Assertions/CommandResultAssertions.cs index f6958a1a5391b..a43b140618950 100644 --- a/src/installer/tests/TestUtils/Assertions/CommandResultAssertions.cs +++ b/src/installer/tests/TestUtils/Assertions/CommandResultAssertions.cs @@ -112,14 +112,14 @@ public AndConstraint HaveStdErrMatching(string pattern, public AndConstraint NotHaveStdOut() { Execute.Assertion.ForCondition(string.IsNullOrEmpty(Result.StdOut)) - .FailWith($"Expected command to not output to stdout but it was not:{GetDiagnosticsInfo()}"); + .FailWith($"Expected command to not output to stdout but it did:{GetDiagnosticsInfo()}"); return new AndConstraint(this); } public AndConstraint NotHaveStdErr() { Execute.Assertion.ForCondition(string.IsNullOrEmpty(Result.StdErr)) - .FailWith($"Expected command to not output to stderr but it was not:{GetDiagnosticsInfo()}"); + .FailWith($"Expected command to not output to stderr but it did:{GetDiagnosticsInfo()}"); return new AndConstraint(this); } diff --git a/src/installer/tests/TestUtils/Constants.cs b/src/installer/tests/TestUtils/Constants.cs index 61363f2eecc87..2a5ce2f53f559 100644 --- a/src/installer/tests/TestUtils/Constants.cs +++ b/src/installer/tests/TestUtils/Constants.cs @@ -113,6 +113,7 @@ public static class DotnetRoot public static class ErrorCode { + public const int Success = 0; public const int InvalidArgFailure = unchecked((int)0x80008081); public const int CoreHostLibMissingFailure = unchecked((int)0x80008083); public const int ResolverInitFailure = unchecked((int)0x8000808b); diff --git a/src/native/corehost/fxr/fx_resolver.cpp b/src/native/corehost/fxr/fx_resolver.cpp index ec6e4f5ed16da..1340af625423c 100644 --- a/src/native/corehost/fxr/fx_resolver.cpp +++ b/src/native/corehost/fxr/fx_resolver.cpp @@ -307,25 +307,6 @@ namespace } } -StatusCode fx_resolver_t::reconcile_fx_references_helper( - const fx_reference_t& lower_fx_ref, - const fx_reference_t& higher_fx_ref, - /*out*/ fx_reference_t& effective_fx_ref) -{ - if (!lower_fx_ref.is_compatible_with_higher_version(higher_fx_ref.get_fx_version_number())) - { - // Error condition - not compatible with the other reference - display_incompatible_framework_error(higher_fx_ref.get_fx_version(), lower_fx_ref); - return StatusCode::FrameworkCompatFailure; - } - - effective_fx_ref = fx_reference_t(higher_fx_ref); // copy - effective_fx_ref.merge_roll_forward_settings_from(lower_fx_ref); - - display_compatible_framework_trace(higher_fx_ref.get_fx_version(), lower_fx_ref); - return StatusCode::Success; -} - // Reconciles two framework references into a new effective framework reference // This process is sometimes also called "soft roll forward" (soft as in no IO) // - fx_ref_a - one of the framework references to reconcile @@ -341,16 +322,24 @@ StatusCode fx_resolver_t::reconcile_fx_references( const fx_reference_t& fx_ref_b, /*out*/ fx_reference_t& effective_fx_ref) { - // The function is split into the helper because the various tracing messages + // Determine which framework reference is higher to do the compat check. The various tracing messages // make more sense if they're always written with higher/lower versions ordered in particular way. - if (fx_ref_a.get_fx_version_number() >= fx_ref_b.get_fx_version_number()) - { - return reconcile_fx_references_helper(fx_ref_b, fx_ref_a, effective_fx_ref); - } - else + bool is_a_higher_than_b = fx_ref_a.get_fx_version_number() >= fx_ref_b.get_fx_version_number(); + const fx_reference_t& lower_fx_ref = is_a_higher_than_b ? fx_ref_b : fx_ref_a; + const fx_reference_t& higher_fx_ref = is_a_higher_than_b ? fx_ref_a : fx_ref_b; + + if (!lower_fx_ref.is_compatible_with_higher_version(higher_fx_ref.get_fx_version_number())) { - return reconcile_fx_references_helper(fx_ref_a, fx_ref_b, effective_fx_ref); + // Error condition - not compatible with the other reference + display_incompatible_framework_error(higher_fx_ref.get_fx_version(), lower_fx_ref); + return StatusCode::FrameworkCompatFailure; } + + effective_fx_ref = fx_reference_t(higher_fx_ref); // copy + effective_fx_ref.merge_roll_forward_settings_from(lower_fx_ref); + + display_compatible_framework_trace(higher_fx_ref.get_fx_version(), lower_fx_ref); + return StatusCode::Success; } void fx_resolver_t::update_newest_references( @@ -415,7 +404,7 @@ StatusCode fx_resolver_t::read_framework( // This reconciles duplicate references to minimize the number of resolve retries. update_newest_references(config); - StatusCode rc = StatusCode::Success; + StatusCode rc; // Loop through each reference and resolve the framework for (const fx_reference_t& original_fx_ref : config.get_frameworks()) @@ -432,23 +421,20 @@ StatusCode fx_resolver_t::read_framework( const fx_reference_t& current_effective_fx_ref = m_effective_fx_references[fx_name]; fx_reference_t new_effective_fx_ref; + // Reconcile the framework reference with the most up to date so far we have for the framework. + // This does not read any physical framework folders yet. + rc = reconcile_fx_references(fx_ref, current_effective_fx_ref, new_effective_fx_ref); + if (rc != StatusCode::Success) + return rc; + auto existing_framework = std::find_if( fx_definitions.begin(), fx_definitions.end(), [&](const std::unique_ptr & fx) { return fx_name == fx->get_name(); }); - if (existing_framework == fx_definitions.end()) { - // Reconcile the framework reference with the most up to date so far we have for the framework. - // This does not read any physical framework folders yet. // Since we didn't find the framework in the resolved list yet, it's OK to update the effective reference // as we haven't processed it yet. - rc = reconcile_fx_references(fx_ref, current_effective_fx_ref, new_effective_fx_ref); - if (rc) - { - break; // Error case - } - m_effective_fx_references[fx_name] = new_effective_fx_ref; // Resolve the effective framework reference against the existing physical framework folders @@ -463,7 +449,7 @@ StatusCode fx_resolver_t::read_framework( app_display_name != nullptr ? app_display_name : host_info.host_path.c_str(), get_current_arch_name()); display_missing_framework_error(fx_name, new_effective_fx_ref.get_fx_version(), pal::string_t(), host_info.dotnet_root, disable_multilevel_lookup); - return FrameworkMissingFailure; + return StatusCode::FrameworkMissingFailure; } // Do NOT update the effective reference to have the same version as the resolved framework. @@ -492,23 +478,13 @@ StatusCode fx_resolver_t::read_framework( } rc = read_framework(host_info, disable_multilevel_lookup, override_settings, new_config, &new_effective_fx_ref, fx_definitions, app_display_name); - if (rc) - { - break; // Error case - } + if (rc != StatusCode::Success) + return rc; } else { - // Reconcile the framework reference with the most up to date so far we have for the framework. - // Note that since we found the framework in the already resolved frameworks - // any update to the effective framework reference needs to restart the resolution process - // so that we re-resolve the framework against disk. - rc = reconcile_fx_references(fx_ref, current_effective_fx_ref, new_effective_fx_ref); - if (rc) - { - break; // Error case - } - + // Since we found the framework in the already resolved frameworks, any update to the effective framework + // reference needs to restart the resolution process so that we re-resolve the framework against disk. if (new_effective_fx_ref != current_effective_fx_ref) { display_retry_framework_trace(current_effective_fx_ref, fx_ref); @@ -522,11 +498,7 @@ StatusCode fx_resolver_t::read_framework( } } - return rc; -} - -fx_resolver_t::fx_resolver_t() -{ + return StatusCode::Success; } StatusCode fx_resolver_t::resolve_frameworks_for_app( diff --git a/src/native/corehost/fxr/fx_resolver.h b/src/native/corehost/fxr/fx_resolver.h index 35c6fd250af5a..018294148f30d 100644 --- a/src/native/corehost/fxr/fx_resolver.h +++ b/src/native/corehost/fxr/fx_resolver.h @@ -27,7 +27,7 @@ class fx_resolver_t const std::unordered_map &existing_framework_versions_by_name); private: - fx_resolver_t(); + fx_resolver_t() = default; void update_newest_references( const runtime_config_t& config); @@ -40,10 +40,6 @@ class fx_resolver_t fx_definition_vector_t& fx_definitions, const pal::char_t* app_display_name); - static StatusCode reconcile_fx_references_helper( - const fx_reference_t& lower_fx_ref, - const fx_reference_t& higher_fx_ref, - /*out*/ fx_reference_t& effective_fx_ref); static StatusCode reconcile_fx_references( const fx_reference_t& fx_ref_a, const fx_reference_t& fx_ref_b,