From 7d673f3378fbf2dd38012a55dd67df86ccfbef65 Mon Sep 17 00:00:00 2001 From: Nate McMaster Date: Sat, 30 Jan 2021 15:26:18 -0800 Subject: [PATCH] cleanup: upgrade test and samples to supported .NET Core versions --- DotNetCorePlugins.sln | 65 +++++++- samples/aspnetcore-mvc/MvcApp/MvcApp.csproj | 2 +- .../MvcAppPlugin1/MvcAppPlugin1.csproj | 2 +- .../DI.HostApp/DI.HostApp.csproj | 2 +- samples/hello-world/HostApp/HostApp.csproj | 2 +- .../HotReloadApp/HotReloadApp.csproj | 2 +- .../TimestampedPlugin.csproj | 2 +- .../Plugins.Tests/BasicAssemblyLoaderTests.cs | 14 +- .../McMaster.NETCore.Plugins.Tests.csproj | 2 +- test/Plugins.Tests/ShadowCopyTests.cs | 59 ++++---- test/TestProjects/NetCoreApp20App/Program.cs | 14 -- .../NetCoreApp2App.csproj} | 2 +- test/TestProjects/NetCoreApp2App/Program.cs | 17 +++ .../WithOwnPlugins/WithOwnPlugins.cs | 141 +++++++++--------- 14 files changed, 199 insertions(+), 127 deletions(-) delete mode 100644 test/TestProjects/NetCoreApp20App/Program.cs rename test/TestProjects/{NetCoreApp20App/NetCoreApp20App.csproj => NetCoreApp2App/NetCoreApp2App.csproj} (80%) create mode 100644 test/TestProjects/NetCoreApp2App/Program.cs diff --git a/DotNetCorePlugins.sln b/DotNetCorePlugins.sln index 1ac5231..d786406 100644 --- a/DotNetCorePlugins.sln +++ b/DotNetCorePlugins.sln @@ -23,7 +23,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JsonNet11", "test\TestProje EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JsonNet9", "test\TestProjects\JsonNet9\JsonNet9.csproj", "{D71D858B-35AF-452F-926F-873DDD990B27}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NetCoreApp20App", "test\TestProjects\NetCoreApp20App\NetCoreApp20App.csproj", "{B9C61145-C010-4FAA-B9EA-4D1AEE20305F}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NetCoreApp2App", "test\TestProjects\NetCoreApp2App\NetCoreApp2App.csproj", "{B9C61145-C010-4FAA-B9EA-4D1AEE20305F}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NetStandardClassLib", "test\TestProjects\NetStandardClassLib\NetStandardClassLib.csproj", "{4FDB7710-F4CA-45FB-9514-A4D288EDA44A}" EndProject @@ -126,6 +126,16 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WithOurPluginsPluginB", "te EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NativeDependency", "test\TestProjects\NativeDependency\NativeDependency.csproj", "{2837A894-11D6-4DBF-B140-84C97C385A15}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "dynamic-implementation", "dynamic-implementation", "{10109B62-DACC-42BA-AD8C-73DE4B93841B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Contracts", "samples\dynamic-implementation\Contracts\Contracts.csproj", "{39B9F0B5-3C53-4140-B158-96C67C03E876}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Host", "samples\dynamic-implementation\Host\Host.csproj", "{703ECD55-EF0E-4734-A28E-371EDD213EEF}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mixer", "samples\dynamic-implementation\Mixer\Mixer.csproj", "{9E2178A0-5915-4308-B6C3-5C8758A47788}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ServiceImplementation", "samples\dynamic-implementation\ServiceImplementation\ServiceImplementation.csproj", "{39BCFE6F-50C4-45AE-8D13-38FB423AB619}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -652,6 +662,54 @@ Global {2837A894-11D6-4DBF-B140-84C97C385A15}.Release|x64.Build.0 = Release|Any CPU {2837A894-11D6-4DBF-B140-84C97C385A15}.Release|x86.ActiveCfg = Release|Any CPU {2837A894-11D6-4DBF-B140-84C97C385A15}.Release|x86.Build.0 = Release|Any CPU + {39B9F0B5-3C53-4140-B158-96C67C03E876}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {39B9F0B5-3C53-4140-B158-96C67C03E876}.Debug|Any CPU.Build.0 = Debug|Any CPU + {39B9F0B5-3C53-4140-B158-96C67C03E876}.Debug|x64.ActiveCfg = Debug|Any CPU + {39B9F0B5-3C53-4140-B158-96C67C03E876}.Debug|x64.Build.0 = Debug|Any CPU + {39B9F0B5-3C53-4140-B158-96C67C03E876}.Debug|x86.ActiveCfg = Debug|Any CPU + {39B9F0B5-3C53-4140-B158-96C67C03E876}.Debug|x86.Build.0 = Debug|Any CPU + {39B9F0B5-3C53-4140-B158-96C67C03E876}.Release|Any CPU.ActiveCfg = Release|Any CPU + {39B9F0B5-3C53-4140-B158-96C67C03E876}.Release|Any CPU.Build.0 = Release|Any CPU + {39B9F0B5-3C53-4140-B158-96C67C03E876}.Release|x64.ActiveCfg = Release|Any CPU + {39B9F0B5-3C53-4140-B158-96C67C03E876}.Release|x64.Build.0 = Release|Any CPU + {39B9F0B5-3C53-4140-B158-96C67C03E876}.Release|x86.ActiveCfg = Release|Any CPU + {39B9F0B5-3C53-4140-B158-96C67C03E876}.Release|x86.Build.0 = Release|Any CPU + {703ECD55-EF0E-4734-A28E-371EDD213EEF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {703ECD55-EF0E-4734-A28E-371EDD213EEF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {703ECD55-EF0E-4734-A28E-371EDD213EEF}.Debug|x64.ActiveCfg = Debug|Any CPU + {703ECD55-EF0E-4734-A28E-371EDD213EEF}.Debug|x64.Build.0 = Debug|Any CPU + {703ECD55-EF0E-4734-A28E-371EDD213EEF}.Debug|x86.ActiveCfg = Debug|Any CPU + {703ECD55-EF0E-4734-A28E-371EDD213EEF}.Debug|x86.Build.0 = Debug|Any CPU + {703ECD55-EF0E-4734-A28E-371EDD213EEF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {703ECD55-EF0E-4734-A28E-371EDD213EEF}.Release|Any CPU.Build.0 = Release|Any CPU + {703ECD55-EF0E-4734-A28E-371EDD213EEF}.Release|x64.ActiveCfg = Release|Any CPU + {703ECD55-EF0E-4734-A28E-371EDD213EEF}.Release|x64.Build.0 = Release|Any CPU + {703ECD55-EF0E-4734-A28E-371EDD213EEF}.Release|x86.ActiveCfg = Release|Any CPU + {703ECD55-EF0E-4734-A28E-371EDD213EEF}.Release|x86.Build.0 = Release|Any CPU + {9E2178A0-5915-4308-B6C3-5C8758A47788}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9E2178A0-5915-4308-B6C3-5C8758A47788}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9E2178A0-5915-4308-B6C3-5C8758A47788}.Debug|x64.ActiveCfg = Debug|Any CPU + {9E2178A0-5915-4308-B6C3-5C8758A47788}.Debug|x64.Build.0 = Debug|Any CPU + {9E2178A0-5915-4308-B6C3-5C8758A47788}.Debug|x86.ActiveCfg = Debug|Any CPU + {9E2178A0-5915-4308-B6C3-5C8758A47788}.Debug|x86.Build.0 = Debug|Any CPU + {9E2178A0-5915-4308-B6C3-5C8758A47788}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9E2178A0-5915-4308-B6C3-5C8758A47788}.Release|Any CPU.Build.0 = Release|Any CPU + {9E2178A0-5915-4308-B6C3-5C8758A47788}.Release|x64.ActiveCfg = Release|Any CPU + {9E2178A0-5915-4308-B6C3-5C8758A47788}.Release|x64.Build.0 = Release|Any CPU + {9E2178A0-5915-4308-B6C3-5C8758A47788}.Release|x86.ActiveCfg = Release|Any CPU + {9E2178A0-5915-4308-B6C3-5C8758A47788}.Release|x86.Build.0 = Release|Any CPU + {39BCFE6F-50C4-45AE-8D13-38FB423AB619}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {39BCFE6F-50C4-45AE-8D13-38FB423AB619}.Debug|Any CPU.Build.0 = Debug|Any CPU + {39BCFE6F-50C4-45AE-8D13-38FB423AB619}.Debug|x64.ActiveCfg = Debug|Any CPU + {39BCFE6F-50C4-45AE-8D13-38FB423AB619}.Debug|x64.Build.0 = Debug|Any CPU + {39BCFE6F-50C4-45AE-8D13-38FB423AB619}.Debug|x86.ActiveCfg = Debug|Any CPU + {39BCFE6F-50C4-45AE-8D13-38FB423AB619}.Debug|x86.Build.0 = Debug|Any CPU + {39BCFE6F-50C4-45AE-8D13-38FB423AB619}.Release|Any CPU.ActiveCfg = Release|Any CPU + {39BCFE6F-50C4-45AE-8D13-38FB423AB619}.Release|Any CPU.Build.0 = Release|Any CPU + {39BCFE6F-50C4-45AE-8D13-38FB423AB619}.Release|x64.ActiveCfg = Release|Any CPU + {39BCFE6F-50C4-45AE-8D13-38FB423AB619}.Release|x64.Build.0 = Release|Any CPU + {39BCFE6F-50C4-45AE-8D13-38FB423AB619}.Release|x86.ActiveCfg = Release|Any CPU + {39BCFE6F-50C4-45AE-8D13-38FB423AB619}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -706,6 +764,11 @@ Global {3A7712FC-9A91-4322-AF7E-4FEC6EB0D845} = {98E964A2-55DA-4740-9F2E-B64FDF6715DB} {D5B3DA06-E60D-4C85-8835-6A26003365AA} = {98E964A2-55DA-4740-9F2E-B64FDF6715DB} {2837A894-11D6-4DBF-B140-84C97C385A15} = {98E964A2-55DA-4740-9F2E-B64FDF6715DB} + {10109B62-DACC-42BA-AD8C-73DE4B93841B} = {F49FFE3B-E1AE-483E-951F-D498F6ABA08E} + {39B9F0B5-3C53-4140-B158-96C67C03E876} = {10109B62-DACC-42BA-AD8C-73DE4B93841B} + {703ECD55-EF0E-4734-A28E-371EDD213EEF} = {10109B62-DACC-42BA-AD8C-73DE4B93841B} + {9E2178A0-5915-4308-B6C3-5C8758A47788} = {10109B62-DACC-42BA-AD8C-73DE4B93841B} + {39BCFE6F-50C4-45AE-8D13-38FB423AB619} = {10109B62-DACC-42BA-AD8C-73DE4B93841B} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {B1AF41DC-A03E-47B1-BBDB-3DC27ABD9F74} diff --git a/samples/aspnetcore-mvc/MvcApp/MvcApp.csproj b/samples/aspnetcore-mvc/MvcApp/MvcApp.csproj index b009a80..b5629f0 100644 --- a/samples/aspnetcore-mvc/MvcApp/MvcApp.csproj +++ b/samples/aspnetcore-mvc/MvcApp/MvcApp.csproj @@ -1,7 +1,7 @@  - netcoreapp3.0 + netcoreapp3.1 diff --git a/samples/aspnetcore-mvc/MvcAppPlugin1/MvcAppPlugin1.csproj b/samples/aspnetcore-mvc/MvcAppPlugin1/MvcAppPlugin1.csproj index 5af4e52..6dfd314 100644 --- a/samples/aspnetcore-mvc/MvcAppPlugin1/MvcAppPlugin1.csproj +++ b/samples/aspnetcore-mvc/MvcAppPlugin1/MvcAppPlugin1.csproj @@ -1,7 +1,7 @@ - netcoreapp3.0 + netcoreapp3.1 true diff --git a/samples/dependency-injection/DI.HostApp/DI.HostApp.csproj b/samples/dependency-injection/DI.HostApp/DI.HostApp.csproj index 9fabc18..186b411 100644 --- a/samples/dependency-injection/DI.HostApp/DI.HostApp.csproj +++ b/samples/dependency-injection/DI.HostApp/DI.HostApp.csproj @@ -2,7 +2,7 @@ Exe - netcoreapp3.0 + netcoreapp3.1 diff --git a/samples/hello-world/HostApp/HostApp.csproj b/samples/hello-world/HostApp/HostApp.csproj index dd1793b..b6efdea 100644 --- a/samples/hello-world/HostApp/HostApp.csproj +++ b/samples/hello-world/HostApp/HostApp.csproj @@ -2,7 +2,7 @@ Exe - netcoreapp3.0 + netcoreapp3.1 diff --git a/samples/hot-reload/HotReloadApp/HotReloadApp.csproj b/samples/hot-reload/HotReloadApp/HotReloadApp.csproj index 45f4204..f69dd88 100644 --- a/samples/hot-reload/HotReloadApp/HotReloadApp.csproj +++ b/samples/hot-reload/HotReloadApp/HotReloadApp.csproj @@ -2,7 +2,7 @@ Exe - netcoreapp3.0 + netcoreapp3.1 diff --git a/samples/hot-reload/TimestampedPlugin/TimestampedPlugin.csproj b/samples/hot-reload/TimestampedPlugin/TimestampedPlugin.csproj index 33a2e28..a700e5f 100644 --- a/samples/hot-reload/TimestampedPlugin/TimestampedPlugin.csproj +++ b/samples/hot-reload/TimestampedPlugin/TimestampedPlugin.csproj @@ -1,7 +1,7 @@ - netcoreapp3.0 + netcoreapp3.1 diff --git a/test/Plugins.Tests/BasicAssemblyLoaderTests.cs b/test/Plugins.Tests/BasicAssemblyLoaderTests.cs index 8b50899..ba5d170 100644 --- a/test/Plugins.Tests/BasicAssemblyLoaderTests.cs +++ b/test/Plugins.Tests/BasicAssemblyLoaderTests.cs @@ -9,11 +9,11 @@ namespace McMaster.NETCore.Plugins.Tests { public class BasicAssemblyLoaderTests { -#if NETCOREAPP3_0 +#if NETCOREAPP3_1 [Fact] public void PluginLoaderCanUnload() { - var path = TestResources.GetTestProjectAssembly("NetCoreApp20App"); + var path = TestResources.GetTestProjectAssembly("NetCoreApp2App"); // See https://github.com/dotnet/coreclr/pull/22221 @@ -36,7 +36,7 @@ namespace McMaster.NETCore.Plugins.Tests var assembly = loader.LoadDefaultAssembly(); var method = assembly - .GetType("NetCoreApp20App.Program", throwOnError: true)! + .GetType("NetCoreApp2App.Program", throwOnError: true)! .GetMethod("GetGreeting", BindingFlags.Static | BindingFlags.Public); Assert.True(loader.IsUnloadable); @@ -81,14 +81,14 @@ namespace McMaster.NETCore.Plugins.Tests } [Fact] - public void LoadsNetCoreApp20Project() + public void LoadsNetCoreApp2Project() { - var path = TestResources.GetTestProjectAssembly("NetCoreApp20App"); + var path = TestResources.GetTestProjectAssembly("NetCoreApp2App"); var loader = PluginLoader.CreateFromAssemblyFile(path); var assembly = loader.LoadDefaultAssembly(); var method = assembly - .GetType("NetCoreApp20App.Program", throwOnError: true)! + .GetType("NetCoreApp2App.Program", throwOnError: true)! .GetMethod("GetGreeting", BindingFlags.Static | BindingFlags.Public); Assert.NotNull(method); Assert.Equal("Hello world!", method!.Invoke(null, Array.Empty())); @@ -144,7 +144,7 @@ namespace McMaster.NETCore.Plugins.Tests { var path = TestResources.GetTestProjectAssembly("Plátano"); var loader = PluginLoader.CreateFromAssemblyFile(path, -#if NETCOREAPP3_0 +#if NETCOREAPP3_1 isUnloadable: true, #endif sharedTypes: new[] { typeof(IFruit) }); diff --git a/test/Plugins.Tests/McMaster.NETCore.Plugins.Tests.csproj b/test/Plugins.Tests/McMaster.NETCore.Plugins.Tests.csproj index 4105ed0..b3b4ec6 100644 --- a/test/Plugins.Tests/McMaster.NETCore.Plugins.Tests.csproj +++ b/test/Plugins.Tests/McMaster.NETCore.Plugins.Tests.csproj @@ -20,7 +20,7 @@ - + diff --git a/test/Plugins.Tests/ShadowCopyTests.cs b/test/Plugins.Tests/ShadowCopyTests.cs index 422b4b0..f16ceee 100644 --- a/test/Plugins.Tests/ShadowCopyTests.cs +++ b/test/Plugins.Tests/ShadowCopyTests.cs @@ -1,28 +1,31 @@ -#if NETCOREAPP3_1 - -using Xunit; - -namespace McMaster.NETCore.Plugins.Tests -{ - public class ShadowCopyTests - { - [Fact] - public void DoesNotThrowWhenLoadingSameNativeDependecyMoreThanOnce() - { - var samplePath = TestResources.GetTestProjectAssembly("NativeDependency"); - - using var loader = PluginLoader - .CreateFromAssemblyFile(samplePath, config => config.EnableHotReload = true); - - var nativeDependecyLoadMethod = loader.LoadDefaultAssembly() - .GetType("NativeDependency.NativeDependencyLoader") - .GetMethod("Load"); - - var exception = Record.Exception(() => nativeDependecyLoadMethod.Invoke(null, null)); - - Assert.Null(exception); - } - } -} - -#endif +// Copyright (c) Nate McMaster. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +#if NETCOREAPP3_1 + +using Xunit; + +namespace McMaster.NETCore.Plugins.Tests +{ + public class ShadowCopyTests + { + [Fact] + public void DoesNotThrowWhenLoadingSameNativeDependecyMoreThanOnce() + { + var samplePath = TestResources.GetTestProjectAssembly("NativeDependency"); + + using var loader = PluginLoader + .CreateFromAssemblyFile(samplePath, config => config.EnableHotReload = true); + + var nativeDependecyLoadMethod = loader.LoadDefaultAssembly() + ?.GetType("NativeDependency.NativeDependencyLoader") + ?.GetMethod("Load"); + + var exception = Record.Exception(() => nativeDependecyLoadMethod?.Invoke(null, null)); + + Assert.Null(exception); + } + } +} + +#endif diff --git a/test/TestProjects/NetCoreApp20App/Program.cs b/test/TestProjects/NetCoreApp20App/Program.cs deleted file mode 100644 index 4deb23a..0000000 --- a/test/TestProjects/NetCoreApp20App/Program.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; - -namespace NetCoreApp20App -{ - class Program - { - static void Main(string[] args) - { - Console.WriteLine(GetGreeting()); - } - - public static string GetGreeting() => "Hello world!"; - } -} diff --git a/test/TestProjects/NetCoreApp20App/NetCoreApp20App.csproj b/test/TestProjects/NetCoreApp2App/NetCoreApp2App.csproj similarity index 80% rename from test/TestProjects/NetCoreApp20App/NetCoreApp20App.csproj rename to test/TestProjects/NetCoreApp2App/NetCoreApp2App.csproj index f6160d0..0403b15 100644 --- a/test/TestProjects/NetCoreApp20App/NetCoreApp20App.csproj +++ b/test/TestProjects/NetCoreApp2App/NetCoreApp2App.csproj @@ -2,7 +2,7 @@ Exe - netcoreapp2.0 + netcoreapp2.1 diff --git a/test/TestProjects/NetCoreApp2App/Program.cs b/test/TestProjects/NetCoreApp2App/Program.cs new file mode 100644 index 0000000..c818479 --- /dev/null +++ b/test/TestProjects/NetCoreApp2App/Program.cs @@ -0,0 +1,17 @@ +// Copyright (c) Nate McMaster. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; + +namespace NetCoreApp2App +{ + internal class Program + { + public static void Main(string[] args) + { + Console.WriteLine(GetGreeting()); + } + + public static string GetGreeting() => "Hello world!"; + } +} diff --git a/test/TestProjects/WithOwnPlugins/WithOwnPlugins.cs b/test/TestProjects/WithOwnPlugins/WithOwnPlugins.cs index c924630..11e30b3 100644 --- a/test/TestProjects/WithOwnPlugins/WithOwnPlugins.cs +++ b/test/TestProjects/WithOwnPlugins/WithOwnPlugins.cs @@ -1,69 +1,72 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Reflection; -using System.Runtime.Loader; -using McMaster.NETCore.Plugins; -using WithOurPluginsPluginContract; -using WithOwnPluginsContract; - -namespace WithOwnPlugins -{ - public class WithOwnPlugins : IWithOwnPlugins - { - public bool TryLoadPluginsInCustomContext(AssemblyLoadContext? callingContext) - { - var currentContext = AssemblyLoadContext.GetLoadContext(Assembly.GetExecutingAssembly()); - if (currentContext == callingContext) - { - throw new ArgumentException("The context of the caller is the context of this assembly. This invalidates the test."); - } - - #if NETCOREAPP3_0 - /* - Ensure the source calling context does not have our plugin's interfaces loaded. - This guarantees that the Assembly cannot possibly unify with the default load context. - - Note: - The code below this check would fail anyway if the assembly would unify with the default context. - This is more of a safety check to ensure "correctness" as opposed to anything else. - */ - var sayHelloAssembly = typeof(ISayHello).Assembly; - if (callingContext?.Assemblies.Contains(sayHelloAssembly) == true) // .Assemblies API not available in Core 2.X - { - throw new ArgumentException("The context of the caller has this plugin's interface to interact with its own plugins loaded. Test is void."); - } - #endif - - // Load our own plugins: Remember, we are in an isolated, non-default ALC. - var plugins = new List(); - string[] assemblyNames = { "Plugins/WithOurPluginsPluginA.dll", "Plugins/WithOurPluginsPluginB.dll" }; - - foreach (var assemblyName in assemblyNames) - { - var currentAssemblyFolderPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) ?? throw new Exception("Unable to get folder path for currently executing assembly."); - var pluginPath = Path.Combine(currentAssemblyFolderPath, assemblyName); - - using var loader = PluginLoader.CreateFromAssemblyFile(pluginPath, new[] { typeof(ISayHello) }); - var assembly = loader.LoadDefaultAssembly(); - var configType = assembly.GetTypes().First(x => typeof(ISayHello).IsAssignableFrom(x) && !x.IsAbstract); - var plugin = (ISayHello?)Activator.CreateInstance(configType); - if (plugin == null) - { - throw new Exception($"Failed to load instance of {nameof(ISayHello)} from plugin."); - } - - plugins.Add(plugin); - } - - // Shouldn't need to check for this but just in case to absolutely make sure. - if (plugins.Any(plugin => String.IsNullOrEmpty(plugin?.SayHello()))) - { - throw new Exception("No value returned from plugin."); - } - - return true; - } - } -} +// Copyright (c) Nate McMaster. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Runtime.Loader; +using McMaster.NETCore.Plugins; +using WithOurPluginsPluginContract; +using WithOwnPluginsContract; + +namespace WithOwnPlugins +{ + public class WithOwnPlugins : IWithOwnPlugins + { + public bool TryLoadPluginsInCustomContext(AssemblyLoadContext? callingContext) + { + var currentContext = AssemblyLoadContext.GetLoadContext(Assembly.GetExecutingAssembly()); + if (currentContext == callingContext) + { + throw new ArgumentException("The context of the caller is the context of this assembly. This invalidates the test."); + } + +#if NETCOREAPP3_1 + /* + Ensure the source calling context does not have our plugin's interfaces loaded. + This guarantees that the Assembly cannot possibly unify with the default load context. + + Note: + The code below this check would fail anyway if the assembly would unify with the default context. + This is more of a safety check to ensure "correctness" as opposed to anything else. + */ + var sayHelloAssembly = typeof(ISayHello).Assembly; + if (callingContext?.Assemblies.Contains(sayHelloAssembly) == true) // .Assemblies API not available in Core 2.X + { + throw new ArgumentException("The context of the caller has this plugin's interface to interact with its own plugins loaded. Test is void."); + } +#endif + + // Load our own plugins: Remember, we are in an isolated, non-default ALC. + var plugins = new List(); + string[] assemblyNames = { "Plugins/WithOurPluginsPluginA.dll", "Plugins/WithOurPluginsPluginB.dll" }; + + foreach (var assemblyName in assemblyNames) + { + var currentAssemblyFolderPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) ?? throw new Exception("Unable to get folder path for currently executing assembly."); + var pluginPath = Path.Combine(currentAssemblyFolderPath, assemblyName); + + using var loader = PluginLoader.CreateFromAssemblyFile(pluginPath, new[] { typeof(ISayHello) }); + var assembly = loader.LoadDefaultAssembly(); + var configType = assembly.GetTypes().First(x => typeof(ISayHello).IsAssignableFrom(x) && !x.IsAbstract); + var plugin = (ISayHello?)Activator.CreateInstance(configType); + if (plugin == null) + { + throw new Exception($"Failed to load instance of {nameof(ISayHello)} from plugin."); + } + + plugins.Add(plugin); + } + + // Shouldn't need to check for this but just in case to absolutely make sure. + if (plugins.Any(plugin => String.IsNullOrEmpty(plugin?.SayHello()))) + { + throw new Exception("No value returned from plugin."); + } + + return true; + } + } +}