Add check for ALC in native hosting tests (#78588)
authorElinor Fung <elfung@microsoft.com>
Mon, 21 Nov 2022 20:10:27 +0000 (12:10 -0800)
committerGitHub <noreply@github.com>
Mon, 21 Nov 2022 20:10:27 +0000 (12:10 -0800)
- Update native hosting tests to check the ALC in which an assembly is executed
- Pull out some helper functions for logging results of calls to hostfxr in host_context_test.cpp

src/installer/tests/Assets/TestProjects/AppWithCustomEntryPoints/Program.cs
src/installer/tests/Assets/TestProjects/ComponentWithNoDependencies/Component.cs
src/installer/tests/HostActivation.Tests/NativeHosting/FunctionPointerResultExtensions.cs [new file with mode: 0644]
src/installer/tests/HostActivation.Tests/NativeHosting/GetFunctionPointer.cs
src/installer/tests/HostActivation.Tests/NativeHosting/LoadAssemblyAndGetFunctionPointer.cs
src/native/corehost/test/nativehost/host_context_test.cpp

index a65e6a9..2fb2088 100644 (file)
@@ -2,12 +2,20 @@
 // The .NET Foundation licenses this file to you under the MIT license.
 
 using System;
+using System.Reflection;
 using System.Runtime.InteropServices;
+using System.Runtime.Loader;
 
 namespace AppWithCustomEntryPoints
 {
     public static class Program
     {
+        static Program()
+        {
+            Assembly asm = Assembly.GetExecutingAssembly();
+            Console.WriteLine($"{asm.GetName().Name}: AssemblyLoadContext = {AssemblyLoadContext.GetLoadContext(asm)}");
+        }
+
         public static void Main(string[] args)
         {
         }
@@ -19,7 +27,10 @@ namespace AppWithCustomEntryPoints
 
         private static void PrintFunctionPointerCallLog(string name, IntPtr arg, int size)
         {
-            Console.WriteLine($"Called {name}(0x{arg.ToString("x")}, {size}) - function pointer call count: {functionPointerCallCount}");
+            Console.WriteLine($"Called {name}(0x{arg.ToString("x")}, {size}) - call count: {functionPointerCallCount}");
+
+            Assembly asm = Assembly.GetExecutingAssembly();
+            Console.WriteLine($"{asm.GetName().Name}: AssemblyLoadContext = {AssemblyLoadContext.GetLoadContext(asm)}");
         }
 
         public static int FunctionPointerEntryPoint1(IntPtr arg, int size)
index 56d41cd..8620ad9 100644 (file)
@@ -2,7 +2,9 @@
 // The .NET Foundation licenses this file to you under the MIT license.
 
 using System;
+using System.Reflection;
 using System.Runtime.InteropServices;
+using System.Runtime.Loader;
 
 namespace Component
 {
@@ -13,9 +15,15 @@ namespace Component
         private static int entryPoint2CallCount = 0;
         private static int unmanagedEntryPoint1CallCount = 0;
 
+        static Component()
+        {
+            Assembly asm = Assembly.GetExecutingAssembly();
+            Console.WriteLine($"{asm.GetName().Name}: AssemblyLoadContext = {AssemblyLoadContext.GetLoadContext(asm)}");
+        }
+
         private static void PrintComponentCallLog(string name, IntPtr arg, int size)
         {
-            Console.WriteLine($"Called {name}(0x{arg.ToString("x")}, {size}) - component call count: {componentCallCount}");
+            Console.WriteLine($"Called {name}(0x{arg.ToString("x")}, {size}) - call count: {componentCallCount}");
         }
 
         public static int ComponentEntryPoint1(IntPtr arg, int size)
diff --git a/src/installer/tests/HostActivation.Tests/NativeHosting/FunctionPointerResultExtensions.cs b/src/installer/tests/HostActivation.Tests/NativeHosting/FunctionPointerResultExtensions.cs
new file mode 100644 (file)
index 0000000..fca9381
--- /dev/null
@@ -0,0 +1,46 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+
+namespace Microsoft.DotNet.CoreSetup.Test.HostActivation.NativeHosting
+{
+    internal static class FunctionPointerResultExtensions
+    {
+        public static FluentAssertions.AndConstraint<CommandResultAssertions> ExecuteFunctionPointer(this CommandResultAssertions assertion, string methodName, int callCount, int returnValue)
+        {
+            return assertion.ExecuteFunctionPointer(methodName, callCount)
+                .And.HaveStdOutContaining($"{methodName} delegate result: 0x{returnValue.ToString("x")}");
+        }
+
+        public static FluentAssertions.AndConstraint<CommandResultAssertions> ExecuteFunctionPointerWithException(this CommandResultAssertions assertion, string methodName, int callCount)
+        {
+            var constraint = assertion.ExecuteFunctionPointer(methodName, callCount);
+            if (OperatingSystem.IsWindows())
+            {
+                return constraint.And.HaveStdOutContaining($"{methodName} delegate threw exception: 0x{Constants.ErrorCode.COMPlusException.ToString("x")}");
+            }
+            else
+            {
+                // Exception is unhandled by native host on non-Windows systems
+                return constraint.And.ExitWith(Constants.ErrorCode.SIGABRT)
+                    .And.HaveStdErrContaining($"Unhandled exception. System.InvalidOperationException: {methodName}");
+            }
+        }
+
+        public static FluentAssertions.AndConstraint<CommandResultAssertions> ExecuteFunctionPointer(this CommandResultAssertions assertion, string methodName, int callCount)
+        {
+            return assertion.HaveStdOutContaining($"Called {methodName}(0xdeadbeef, 42) - call count: {callCount}");
+        }
+
+        public static FluentAssertions.AndConstraint<CommandResultAssertions> ExecuteInIsolatedContext(this CommandResultAssertions assertion, string assemblyName)
+        {
+            return assertion.HaveStdOutContaining($"{assemblyName}: AssemblyLoadContext = \"IsolatedComponentLoadContext(");
+        }
+
+        public static FluentAssertions.AndConstraint<CommandResultAssertions> ExecuteInDefaultContext(this CommandResultAssertions assertion, string assemblyName)
+        {
+            return assertion.HaveStdOutContaining($"{assemblyName}: AssemblyLoadContext = \"Default\" System.Runtime.Loader.DefaultAssemblyLoadContext");
+        }
+    }
+}
index 264ff90..85ab3e5 100644 (file)
@@ -47,7 +47,8 @@ namespace Microsoft.DotNet.CoreSetup.Test.HostActivation.NativeHosting
             if (validType && validMethod)
             {
                 result.Should().Pass()
-                    .And.ExecuteFunctionPointer(sharedState.FunctionPointerEntryPoint1, 1, 1);
+                    .And.ExecuteFunctionPointer(sharedState.FunctionPointerEntryPoint1, 1, 1)
+                    .And.ExecuteInDefaultContext(appProject.AssemblyName);
             }
             else
             {
@@ -96,10 +97,10 @@ namespace Microsoft.DotNet.CoreSetup.Test.HostActivation.NativeHosting
             CommandResult result = sharedState.CreateNativeHostCommand(args, sharedState.DotNetRoot)
                 .Execute();
 
-            result.Should()
-                .InitializeContextForApp(appProject.AppDll)
-                .And.Pass()
-                .And.ExecuteFunctionPointer(sharedState.FunctionPointerEntryPoint1, 1, 1);
+            result.Should().Pass()
+                .And.InitializeContextForApp(appProject.AppDll)
+                .And.ExecuteFunctionPointer(sharedState.FunctionPointerEntryPoint1, 1, 1)
+                .And.ExecuteInDefaultContext(appProject.AssemblyName);
         }
 
         [Theory]
@@ -138,7 +139,8 @@ namespace Microsoft.DotNet.CoreSetup.Test.HostActivation.NativeHosting
                 .Execute();
 
             result.Should().Pass()
-                .And.InitializeContextForApp(appProject.AppDll);
+                .And.InitializeContextForApp(appProject.AppDll)
+                .And.ExecuteInDefaultContext(appProject.AssemblyName);
 
             for (int i = 1; i <= callCount; ++i)
             {
@@ -179,7 +181,8 @@ namespace Microsoft.DotNet.CoreSetup.Test.HostActivation.NativeHosting
                 .Execute();
 
             result.Should().Pass()
-                .And.InitializeContextForApp(appProject.AppDll);
+                .And.InitializeContextForApp(appProject.AppDll)
+                .And.ExecuteInDefaultContext(appProject.AssemblyName);
 
             for (int i = 1; i <= callCount; ++i)
             {
@@ -257,33 +260,4 @@ namespace Microsoft.DotNet.CoreSetup.Test.HostActivation.NativeHosting
             }
         }
     }
-
-    internal static class FunctionPointerLoadingResultExtensions
-    {
-        public static FluentAssertions.AndConstraint<CommandResultAssertions> ExecuteFunctionPointer(this CommandResultAssertions assertion, string methodName, int functionPointerCallCount, int returnValue)
-        {
-            return assertion.ExecuteFunctionPointer(methodName, functionPointerCallCount)
-                .And.HaveStdOutContaining($"{methodName} delegate result: 0x{returnValue.ToString("x")}");
-        }
-
-        public static FluentAssertions.AndConstraint<CommandResultAssertions> ExecuteFunctionPointerWithException(this CommandResultAssertions assertion, string methodName, int functionPointerCallCount)
-        {
-            var constraint = assertion.ExecuteFunctionPointer(methodName, functionPointerCallCount);
-            if (OperatingSystem.IsWindows())
-            {
-                return constraint.And.HaveStdOutContaining($"{methodName} delegate threw exception: 0x{Constants.ErrorCode.COMPlusException.ToString("x")}");
-            }
-            else
-            {
-                // Exception is unhandled by native host on non-Windows systems
-                return constraint.And.ExitWith(Constants.ErrorCode.SIGABRT)
-                    .And.HaveStdErrContaining($"Unhandled exception. System.InvalidOperationException: {methodName}");
-            }
-        }
-
-        public static FluentAssertions.AndConstraint<CommandResultAssertions> ExecuteFunctionPointer(this CommandResultAssertions assertion, string methodName, int functionPointerCallCount)
-        {
-            return assertion.HaveStdOutContaining($"Called {methodName}(0xdeadbeef, 42) - function pointer call count: {functionPointerCallCount}");
-        }
-    }
 }
index 34ac3ef..653a44e 100644 (file)
@@ -49,7 +49,8 @@ namespace Microsoft.DotNet.CoreSetup.Test.HostActivation.NativeHosting
             if (validPath && validType && validMethod)
             {
                 result.Should().Pass()
-                    .And.ExecuteComponentEntryPoint(sharedState.ComponentEntryPoint1, 1, 1);
+                    .And.ExecuteFunctionPointer(sharedState.ComponentEntryPoint1, 1, 1)
+                    .And.ExecuteInIsolatedContext(componentProject.AssemblyName);
             }
             else
             {
@@ -84,7 +85,8 @@ namespace Microsoft.DotNet.CoreSetup.Test.HostActivation.NativeHosting
             if (validPath && validType && validMethod)
             {
                 result.Should().Pass()
-                    .And.ExecuteComponentEntryPoint(sharedState.ComponentEntryPoint1, 1, 1);
+                    .And.ExecuteFunctionPointer(sharedState.ComponentEntryPoint1, 1, 1)
+                    .And.ExecuteInIsolatedContext(componentProject.AssemblyName);
             }
             else
             {
@@ -112,7 +114,8 @@ namespace Microsoft.DotNet.CoreSetup.Test.HostActivation.NativeHosting
             result.Should()
                 .InitializeContextForApp(appProject.AppDll)
                 .And.Pass()
-                .And.ExecuteComponentEntryPoint(sharedState.ComponentEntryPoint1, 1, 1);
+                .And.ExecuteFunctionPointer(sharedState.ComponentEntryPoint1, 1, 1)
+                .And.ExecuteInIsolatedContext(componentProject.AssemblyName);
         }
 
         [Theory]
@@ -153,13 +156,14 @@ namespace Microsoft.DotNet.CoreSetup.Test.HostActivation.NativeHosting
                 .Execute();
 
             result.Should().Pass()
-                .And.InitializeContextForConfig(componentProject.RuntimeConfigJson);
+                .And.InitializeContextForConfig(componentProject.RuntimeConfigJson)
+                .And.ExecuteInIsolatedContext(componentProject.AssemblyName);
 
             for (int i = 1; i <= callCount; ++i)
             {
                 result.Should()
-                    .ExecuteComponentEntryPoint(comp1Name, i * 2 - 1, i)
-                    .And.ExecuteComponentEntryPoint(sharedState.ComponentEntryPoint2, i * 2, i);
+                    .ExecuteFunctionPointer(comp1Name, i * 2 - 1, i)
+                    .And.ExecuteFunctionPointer(sharedState.ComponentEntryPoint2, i * 2, i);
             }
         }
 
@@ -197,13 +201,14 @@ namespace Microsoft.DotNet.CoreSetup.Test.HostActivation.NativeHosting
                 .Execute();
 
             result.Should().Pass()
-                .And.InitializeContextForConfig(componentProject.RuntimeConfigJson);
+                .And.InitializeContextForConfig(componentProject.RuntimeConfigJson)
+                .And.ExecuteInIsolatedContext(componentProject.AssemblyName);
 
             for (int i = 1; i <= callCount; ++i)
             {
                 result.Should()
-                    .ExecuteComponentEntryPoint(sharedState.ComponentEntryPoint1, i, i)
-                    .And.ExecuteComponentEntryPoint(sharedState.ComponentEntryPoint2, i, i);
+                    .ExecuteFunctionPointer(sharedState.ComponentEntryPoint1, i, i)
+                    .And.ExecuteFunctionPointer(sharedState.ComponentEntryPoint2, i, i);
             }
         }
 
@@ -226,7 +231,7 @@ namespace Microsoft.DotNet.CoreSetup.Test.HostActivation.NativeHosting
                 .Execute(expectedToFail: true)
                 .Should().Fail()
                 .And.InitializeContextForConfig(componentProject.RuntimeConfigJson)
-                .And.ExecuteComponentEntryPointWithException(entryPoint, 1);
+                .And.ExecuteFunctionPointerWithException(entryPoint, 1);
         }
 
         public class SharedTestState : SharedTestStateBase
@@ -273,33 +278,4 @@ namespace Microsoft.DotNet.CoreSetup.Test.HostActivation.NativeHosting
             }
         }
     }
-
-    internal static class ComponentActivationResultExtensions
-    {
-        public static FluentAssertions.AndConstraint<CommandResultAssertions> ExecuteComponentEntryPoint(this CommandResultAssertions assertion, string methodName, int componentCallCount, int returnValue)
-        {
-            return assertion.ExecuteComponentEntryPoint(methodName, componentCallCount)
-                .And.HaveStdOutContaining($"{methodName} delegate result: 0x{returnValue.ToString("x")}");
-        }
-
-        public static FluentAssertions.AndConstraint<CommandResultAssertions> ExecuteComponentEntryPointWithException(this CommandResultAssertions assertion, string methodName, int componentCallCount)
-        {
-            var constraint = assertion.ExecuteComponentEntryPoint(methodName, componentCallCount);
-            if (OperatingSystem.IsWindows())
-            {
-                return constraint.And.HaveStdOutContaining($"{methodName} delegate threw exception: 0x{Constants.ErrorCode.COMPlusException.ToString("x")}");
-            }
-            else
-            {
-                // Exception is unhandled by native host on non-Windows systems
-                return constraint.And.ExitWith(Constants.ErrorCode.SIGABRT)
-                    .And.HaveStdErrContaining($"Unhandled exception. System.InvalidOperationException: {methodName}");
-            }
-        }
-
-        public static FluentAssertions.AndConstraint<CommandResultAssertions> ExecuteComponentEntryPoint(this CommandResultAssertions assertion, string methodName, int componentCallCount)
-        {
-            return assertion.HaveStdOutContaining($"Called {methodName}(0xdeadbeef, 42) - component call count: {componentCallCount}");
-        }
-    }
 }
index 9cc84b3..2b98d9c 100644 (file)
@@ -142,6 +142,47 @@ namespace
         }
     }
 
+    bool init_for_config(
+        const hostfxr_exports &hostfxr,
+        const pal::char_t *config_path,
+        hostfxr_handle* handle,
+        const pal::char_t *log_prefix,
+        pal::stringstream_t &test_output)
+    {
+        int rc = hostfxr.init_config(config_path, nullptr, handle);
+        bool success = STATUS_CODE_SUCCEEDED(rc);
+        test_output << log_prefix << _X("hostfxr_initialize_for_runtime_config ") << (success ? _X("succeeded: ") : _X("failed: ")) << std::hex << std::showbase << rc << std::endl;
+        return success;
+    }
+
+    bool init_for_command_line(
+        const hostfxr_exports &hostfxr,
+        int argc,
+        const pal::char_t *argv[],
+        hostfxr_handle* handle,
+        const pal::char_t *log_prefix,
+        pal::stringstream_t &test_output)
+    {
+        int rc = hostfxr.init_command_line(argc, argv, nullptr, handle);
+        bool success = rc == StatusCode::Success;
+        test_output << log_prefix << _X("hostfxr_initialize_for_command_line ") << (success ? _X("succeeded: ") : _X("failed: ")) << std::hex << std::showbase << rc << std::endl;
+        return success;
+    }
+
+    bool get_runtime_delegate(
+        const hostfxr_exports &hostfxr,
+        hostfxr_handle handle,
+        hostfxr_delegate_type delegate_type,
+        void** delegate,
+        const pal::char_t *log_prefix,
+        pal::stringstream_t &test_output)
+    {
+        int rc = hostfxr.get_delegate(handle, delegate_type, delegate);
+        bool success = rc == StatusCode::Success;
+        test_output << log_prefix << _X("hostfxr_get_runtime_delegate ") << (success ? _X("succeeded: ") : _X("failed: ")) << std::hex << std::showbase << rc << std::endl;
+        return success;
+    }
+
     bool config_test(
         const hostfxr_exports &hostfxr,
         host_context_test::check_properties check_properties,
@@ -153,30 +194,22 @@ namespace
         pal::stringstream_t &test_output)
     {
         hostfxr_handle handle;
-        int rc = hostfxr.init_config(config_path, nullptr, &handle);
-        if (!STATUS_CODE_SUCCEEDED(rc))
-        {
-            test_output << log_prefix << _X("hostfxr_initialize_for_runtime_config failed: ") << std::hex << std::showbase << rc << std::endl;
+        if (!init_for_config(hostfxr, config_path, &handle, log_prefix, test_output))
             return false;
-        }
-
-        test_output << log_prefix << _X("hostfxr_initialize_for_runtime_config succeeded: ") << std::hex << std::showbase << rc << std::endl;
 
         inspect_modify_properties(check_properties, hostfxr, handle, argc, argv, log_prefix, test_output);
 
         void *delegate;
-        rc = hostfxr.get_delegate(handle, delegate_type, &delegate);
-        if (rc != StatusCode::Success)
-            test_output << log_prefix << _X("hostfxr_get_runtime_delegate failed: ") << std::hex << std::showbase << rc << std::endl;
+        bool success = get_runtime_delegate(hostfxr, handle, delegate_type, &delegate, log_prefix, test_output);
 
         int rcClose = hostfxr.close(handle);
         if (rcClose != StatusCode::Success)
             test_output << log_prefix << _X("hostfxr_close failed: ") << std::hex << std::showbase << rcClose << std::endl;
 
-        return rc == StatusCode::Success && rcClose == StatusCode::Success;
+        return success && rcClose == StatusCode::Success;
     }
 
-    int run_app_with_try_except(
+    bool run_app_with_try_except(
         hostfxr_run_app_fn run_app,
         const hostfxr_handle handle,
         pal::stringstream_t &test_output)
@@ -189,7 +222,7 @@ namespace
             if (rc != StatusCode::Success)
                 test_output << _X("hostfxr_run_app failed: ") << std::hex << std::showbase << rc << std::endl;
 
-            return rc;
+            return rc == StatusCode::Success;
         }
 #if defined(WIN32)
         __except(GetExceptionCode() != 0)
@@ -198,10 +231,10 @@ namespace
         }
 #endif
 
-        return -1;
+        return false;
     }
 
-    int call_delegate_with_try_except(
+    bool call_delegate_with_try_except(
         component_entry_point_fn component_entry_point,
         const pal::char_t *method_name,
         const pal::char_t *log_prefix,
@@ -214,7 +247,7 @@ namespace
             int result = component_entry_point((void*)(static_cast<size_t>(0xdeadbeef)), 42);
             test_output << log_prefix << method_name << _X(" delegate result: ") << std::hex << std::showbase << result << std::endl;
 
-            return StatusCode::Success;
+            return true;
         }
 #if defined(WIN32)
         __except(GetExceptionCode() != 0)
@@ -223,7 +256,7 @@ namespace
         }
 #endif
 
-        return -1;
+        return false;
     }
 
     struct _printable_delegate_name_t
@@ -252,7 +285,7 @@ namespace
         return _printable_delegate_name_t{ delegate_name };
     }
 
-    int call_load_assembly_and_get_function_pointer_flavour(
+    bool call_load_assembly_and_get_function_pointer_flavour(
         load_assembly_and_get_function_pointer_fn delegate,
         const pal::char_t *assembly_path,
         const pal::char_t *type_name,
@@ -280,21 +313,15 @@ namespace
                         delegate_name,
                         nullptr /* reserved */,
                         (void **)&componentEntryPointDelegate);
+        bool success = rc == StatusCode::Success;
+        test_output << log_prefix << _X("load_assembly_and_get_function_pointer ") << (success ? _X("succeeded: ") : _X("failed: ")) << std::hex << std::showbase << rc << std::endl;
+        if (success)
+            success &= call_delegate_with_try_except(componentEntryPointDelegate, method_name, log_prefix, test_output);
 
-        if (rc != StatusCode::Success)
-        {
-            test_output << log_prefix << _X("load_assembly_and_get_function_pointer failed: ") << std::hex << std::showbase << rc << std::endl;
-        }
-        else
-        {
-            test_output << log_prefix << _X("load_assembly_and_get_function_pointer succeeded: ") << std::hex << std::showbase << rc << std::endl;
-            rc = call_delegate_with_try_except(componentEntryPointDelegate, method_name, log_prefix, test_output);
-        }
-
-        return rc;
+        return success;
     }
 
-    int call_get_function_pointer_flavour(
+    bool call_get_function_pointer_flavour(
         get_function_pointer_fn delegate,
         const pal::char_t *type_name,
         const pal::char_t *method_name,
@@ -321,17 +348,12 @@ namespace
                           nullptr /* reserved */,
                           (void **)&functionPointerDelegate);
 
-        if (rc != StatusCode::Success)
-        {
-            test_output << log_prefix << _X("get_function_pointer failed: ") << std::hex << std::showbase << rc << std::endl;
-        }
-        else
-        {
-            test_output << log_prefix << _X("get_function_pointer succeeded: ") << std::hex << std::showbase << rc << std::endl;
-            rc = call_delegate_with_try_except(functionPointerDelegate, method_name, log_prefix, test_output);
-        }
+        bool success = rc == StatusCode::Success;
+        test_output << log_prefix << _X("get_function_pointer ") << (success ? _X("succeeded: ") : _X("failed: ")) << std::hex << std::showbase << rc << std::endl;
+        if (success)
+            success &= call_delegate_with_try_except(functionPointerDelegate, method_name, log_prefix, test_output);
 
-        return rc;
+        return success;
     }
 
     bool component_load_assembly_and_get_function_pointer_test(
@@ -343,31 +365,20 @@ namespace
         pal::stringstream_t &test_output)
     {
         hostfxr_handle handle;
-        int rc = hostfxr.init_config(config_path, nullptr, &handle);
-        if (!STATUS_CODE_SUCCEEDED(rc))
-        {
-            test_output << log_prefix << _X("hostfxr_initialize_for_runtime_config failed: ") << std::hex << std::showbase << rc << std::endl;
+        if (!init_for_config(hostfxr, config_path, &handle, log_prefix, test_output))
             return false;
-        }
-
-        test_output << log_prefix << _X("hostfxr_initialize_for_runtime_config succeeded: ") << std::hex << std::showbase << rc << std::endl;
 
-        for (int i = 0; i <= argc - 3; i += 3)
+        load_assembly_and_get_function_pointer_fn delegate = nullptr;
+        hostfxr_delegate_type hdt = hostfxr_delegate_type::hdt_load_assembly_and_get_function_pointer;
+        bool success = get_runtime_delegate(hostfxr, handle, hdt, (void **)&delegate, log_prefix, test_output);
+        if (success)
         {
-            const pal::char_t *assembly_path = argv[i];
-            const pal::char_t *type_name = argv[i + 1];
-            const pal::char_t *method_name = argv[i + 2];
-
-            load_assembly_and_get_function_pointer_fn delegate = nullptr;
-            rc = hostfxr.get_delegate(handle, hostfxr_delegate_type::hdt_load_assembly_and_get_function_pointer, (void **)&delegate);
-            if (rc != StatusCode::Success)
-            {
-                test_output << log_prefix << _X("hostfxr_get_runtime_delegate failed: ") << std::hex << std::showbase << rc << std::endl;
-            }
-            else
+            for (int i = 0; i <= argc - 3; i += 3)
             {
-                test_output << log_prefix << _X("hostfxr_get_runtime_delegate succeeded: ") << std::hex << std::showbase << rc << std::endl;
-                rc = call_load_assembly_and_get_function_pointer_flavour(delegate, assembly_path, type_name, method_name, log_prefix, test_output);
+                const pal::char_t *assembly_path = argv[i];
+                const pal::char_t *type_name = argv[i + 1];
+                const pal::char_t *method_name = argv[i + 2];
+                success &= call_load_assembly_and_get_function_pointer_flavour(delegate, assembly_path, type_name, method_name, log_prefix, test_output);
             }
         }
 
@@ -375,7 +386,7 @@ namespace
         if (rcClose != StatusCode::Success)
             test_output << log_prefix << _X("hostfxr_close failed: ") << std::hex << std::showbase << rcClose << std::endl;
 
-        return rc == StatusCode::Success && rcClose == StatusCode::Success;
+        return success && rcClose == StatusCode::Success;
     }
 
     bool app_load_assembly_and_get_function_pointer_test(
@@ -386,31 +397,20 @@ namespace
         pal::stringstream_t &test_output)
     {
         hostfxr_handle handle;
-        int rc = hostfxr.init_command_line(argc, argv, nullptr, &handle);
-        if (rc != StatusCode::Success)
-        {
-            test_output << _X("hostfxr_initialize_for_command_line failed: ") << std::hex << std::showbase << rc << std::endl;
+        if (!init_for_command_line(hostfxr, argc, argv, &handle, log_prefix, test_output))
             return false;
-        }
 
-        test_output << log_prefix << _X("hostfxr_initialize_for_command_line succeeded: ") << std::hex << std::showbase << rc << std::endl;
-
-        for (int i = 1; i <= argc - 3; i += 3)
+        load_assembly_and_get_function_pointer_fn delegate = nullptr;
+        hostfxr_delegate_type hdt = hostfxr_delegate_type::hdt_load_assembly_and_get_function_pointer;
+        bool success = get_runtime_delegate(hostfxr, handle, hdt, (void **)&delegate, log_prefix, test_output);
+        if (success)
         {
-            const pal::char_t *assembly_path = argv[i];
-            const pal::char_t *type_name = argv[i + 1];
-            const pal::char_t *method_name = argv[i + 2];
-
-            load_assembly_and_get_function_pointer_fn delegate = nullptr;
-            rc = hostfxr.get_delegate(handle, hostfxr_delegate_type::hdt_load_assembly_and_get_function_pointer, (void **)&delegate);
-            if (rc != StatusCode::Success)
+            for (int i = 1; i <= argc - 3; i += 3)
             {
-                test_output << log_prefix << _X("hostfxr_get_runtime_delegate failed: ") << std::hex << std::showbase << rc << std::endl;
-            }
-            else
-            {
-                test_output << log_prefix << _X("hostfxr_get_runtime_delegate succeeded: ") << std::hex << std::showbase << rc << std::endl;
-                rc = call_load_assembly_and_get_function_pointer_flavour(delegate, assembly_path, type_name, method_name, log_prefix, test_output);
+                const pal::char_t *assembly_path = argv[i];
+                const pal::char_t *type_name = argv[i + 1];
+                const pal::char_t *method_name = argv[i + 2];
+                success &= call_load_assembly_and_get_function_pointer_flavour(delegate, assembly_path, type_name, method_name, log_prefix, test_output);
             }
         }
 
@@ -418,7 +418,7 @@ namespace
         if (rcClose != StatusCode::Success)
             test_output << log_prefix << _X("hostfxr_close failed: ") << std::hex << std::showbase << rcClose << std::endl;
 
-        return rc == StatusCode::Success && rcClose == StatusCode::Success;
+        return success && rcClose == StatusCode::Success;
     }
 
     bool component_get_function_pointer_test(
@@ -430,30 +430,19 @@ namespace
         pal::stringstream_t &test_output)
     {
         hostfxr_handle handle;
-        int rc = hostfxr.init_config(config_path, nullptr, &handle);
-        if (!STATUS_CODE_SUCCEEDED(rc))
-        {
-            test_output << log_prefix << _X("hostfxr_initialize_for_runtime_config failed: ") << std::hex << std::showbase << rc << std::endl;
+        if (!init_for_config(hostfxr, config_path, &handle, log_prefix, test_output))
             return false;
-        }
-
-        test_output << log_prefix << _X("hostfxr_initialize_for_runtime_config succeeded: ") << std::hex << std::showbase << rc << std::endl;
 
-        for (int i = 0; i <= argc - 2; i += 2)
+        get_function_pointer_fn delegate = nullptr;
+        hostfxr_delegate_type hdt = hostfxr_delegate_type::hdt_get_function_pointer;
+        bool success = get_runtime_delegate(hostfxr, handle, hdt, (void **)&delegate, log_prefix, test_output);
+        if (success)
         {
-            const pal::char_t *type_name = argv[i];
-            const pal::char_t *method_name = argv[i + 1];
-
-            get_function_pointer_fn delegate = nullptr;
-            rc = hostfxr.get_delegate(handle, hostfxr_delegate_type::hdt_get_function_pointer, (void **)&delegate);
-            if (rc != StatusCode::Success)
-            {
-                test_output << log_prefix << _X("hostfxr_get_runtime_delegate failed: ") << std::hex << std::showbase << rc << std::endl;
-            }
-            else
+            for (int i = 0; i <= argc - 2; i += 2)
             {
-                test_output << log_prefix << _X("hostfxr_get_runtime_delegate succeeded: ") << std::hex << std::showbase << rc << std::endl;
-                rc = call_get_function_pointer_flavour(delegate, type_name, method_name, log_prefix, test_output);
+                const pal::char_t *type_name = argv[i];
+                const pal::char_t *method_name = argv[i + 1];
+                success &= call_get_function_pointer_flavour(delegate, type_name, method_name, log_prefix, test_output);
             }
         }
 
@@ -461,7 +450,7 @@ namespace
         if (rcClose != StatusCode::Success)
             test_output << log_prefix << _X("hostfxr_close failed: ") << std::hex << std::showbase << rcClose << std::endl;
 
-        return rc == StatusCode::Success && rcClose == StatusCode::Success;
+        return success && rcClose == StatusCode::Success;
     }
 
     bool app_get_function_pointer_test(
@@ -472,30 +461,19 @@ namespace
         pal::stringstream_t &test_output)
     {
         hostfxr_handle handle;
-        int rc = hostfxr.init_command_line(argc, argv, nullptr, &handle);
-        if (rc != StatusCode::Success)
-        {
-            test_output << _X("hostfxr_initialize_for_command_line failed: ") << std::hex << std::showbase << rc << std::endl;
+        if (!init_for_command_line(hostfxr, argc, argv, &handle, log_prefix, test_output))
             return false;
-        }
 
-        test_output << log_prefix << _X("hostfxr_initialize_for_command_line succeeded: ") << std::hex << std::showbase << rc << std::endl;
-
-        for (int i = 1; i <= argc - 2; i += 2)
+        get_function_pointer_fn delegate = nullptr;
+        hostfxr_delegate_type hdt = hostfxr_delegate_type::hdt_get_function_pointer;
+        bool success = get_runtime_delegate(hostfxr, handle, hdt, (void **)&delegate, log_prefix, test_output);
+        if (success)
         {
-            const pal::char_t *type_name = argv[i];
-            const pal::char_t *method_name = argv[i + 1];
-
-            get_function_pointer_fn delegate = nullptr;
-            rc = hostfxr.get_delegate(handle, hostfxr_delegate_type::hdt_get_function_pointer, (void **)&delegate);
-            if (rc != StatusCode::Success)
-            {
-                test_output << log_prefix << _X("hostfxr_get_runtime_delegate failed: ") << std::hex << std::showbase << rc << std::endl;
-            }
-            else
+            for (int i = 1; i <= argc - 2; i += 2)
             {
-                test_output << log_prefix << _X("hostfxr_get_runtime_delegate succeeded: ") << std::hex << std::showbase << rc << std::endl;
-                rc = call_get_function_pointer_flavour(delegate, type_name, method_name, log_prefix, test_output);
+                const pal::char_t *type_name = argv[i];
+                const pal::char_t *method_name = argv[i + 1];
+                success &= call_get_function_pointer_flavour(delegate, type_name, method_name, log_prefix, test_output);
             }
         }
 
@@ -503,7 +481,7 @@ namespace
         if (rcClose != StatusCode::Success)
             test_output << log_prefix << _X("hostfxr_close failed: ") << std::hex << std::showbase << rcClose << std::endl;
 
-        return rc == StatusCode::Success && rcClose == StatusCode::Success;
+        return success && rcClose == StatusCode::Success;
     }
 }
 
@@ -547,22 +525,18 @@ bool host_context_test::app(
     hostfxr_exports hostfxr { hostfxr_path };
 
     hostfxr_handle handle;
-    int rc = hostfxr.init_command_line(argc, argv, nullptr, &handle);
-    if (rc != StatusCode::Success)
-    {
-        test_output << _X("hostfxr_initialize_for_command_line failed: ") << std::hex << std::showbase << rc << std::endl;
+    if (!init_for_command_line(hostfxr, argc, argv, &handle, app_log_prefix, test_output))
         return false;
-    }
 
     inspect_modify_properties(check_properties, hostfxr, handle, argc, argv, app_log_prefix, test_output);
 
-    rc = run_app_with_try_except(hostfxr.run_app, handle, test_output);
+    bool success = run_app_with_try_except(hostfxr.run_app, handle, test_output);
 
     int rcClose = hostfxr.close(handle);
     if (rcClose != StatusCode::Success)
         test_output << _X("hostfxr_close failed: ") << std::hex << std::showbase << rcClose << std::endl;
 
-    return rc == StatusCode::Success && rcClose == StatusCode::Success;
+    return success && rcClose == StatusCode::Success;
 }
 
 bool host_context_test::config(
@@ -654,12 +628,8 @@ bool host_context_test::mixed(
         argv_local.push_back(argv[i]);
 
     hostfxr_handle handle;
-    int rc = hostfxr.init_command_line(static_cast<int32_t>(argv_local.size()), argv_local.data(), nullptr, &handle);
-    if (rc != StatusCode::Success)
-    {
-        test_output << _X("hostfxr_initialize_for_command_line failed: ") << std::hex << std::showbase << rc << std::endl;
+    if (!init_for_command_line(hostfxr, static_cast<int32_t>(argv_local.size()), argv_local.data(), &handle, app_log_prefix, test_output))
         return false;
-    }
 
     inspect_modify_properties(check_properties, hostfxr, handle, argc, argv, app_log_prefix, test_output);