using Moq; using Prise.AssemblyLoading; using Prise.Tests.Plugins; using System; using System.IO; using System.Reflection; using System.Runtime.Loader; using System.Threading.Tasks; namespace Prise.Tests.AssemblyLoading.DefaultAssemblyLoadContextTests { public class AssemblyLoadContextWithLoadedPluginTestContext { public string PluginAssemblyPath { get; set; } public string InitialPluginLoadDirectory { get; set; } public string NewtonsoftAssemblyPath { get; set; } public AssemblyName NewtonsoftAssemblyName { get; set; } public Assembly NewtonsoftAssembly { get; set; } public IPluginLoadContext PluginLoadContext { get; set; } public TestContext TestContext { get; set; } public IAssemblyLoadContext Sut() => this.TestContext.Sut(); public Mock GetMock() where M : class { return this.TestContext.GetMock(); } } public class TestWithLoadedPluginBase : Base { protected async Task SetupLoadedPluginTextContext(Action configure = null) { var testContext = SetupAssemblyLoadContext(); var loadContext = testContext.Sut(); var fileSystemUtility = testContext.GetMock(); var assemblyLoadStrategy = testContext.GetMock(); var pluginDependencyContext = testContext.GetMock(); var pluginDependencyProvider = testContext.GetMock(); var pluginAssemblyPath = Path.Combine(GetPathToAssemblies(), "Prise.Tests.dll"); var initialPluginLoadDirectory = Path.GetDirectoryName(pluginAssemblyPath); var newtonsoftAssemblyPath = Path.Combine(GetPathToAssemblies(), "Newtonsoft.Json.dll"); var newtonsoftAssembly = Assembly.LoadFile(newtonsoftAssemblyPath); var newtonsoftAssemblyName = AssemblyLoadContext.GetAssemblyName(newtonsoftAssemblyPath); var assemblyStream = File.OpenRead(pluginAssemblyPath); var pluginLoadContext = GetPluginLoadContext(pluginAssemblyPath, configure); fileSystemUtility.Setup(f => f.EnsureFileExists(pluginAssemblyPath)).Returns(pluginAssemblyPath); fileSystemUtility.Setup(f => f.ReadFileFromDisk(pluginAssemblyPath)).ReturnsAsync(assemblyStream); pluginDependencyProvider.Setup(p => p.FromPluginLoadContext(pluginLoadContext)).ReturnsAsync(pluginDependencyContext.Object); // This must be invoked before anything else can be tested await loadContext.LoadPluginAssembly(pluginLoadContext); return new AssemblyLoadContextWithLoadedPluginTestContext { TestContext = testContext, InitialPluginLoadDirectory = initialPluginLoadDirectory, PluginAssemblyPath = pluginAssemblyPath, PluginLoadContext = pluginLoadContext, NewtonsoftAssembly = newtonsoftAssembly, NewtonsoftAssemblyName = newtonsoftAssemblyName, NewtonsoftAssemblyPath = newtonsoftAssemblyPath, }; } } public class Base : TestBase { protected T InvokeProtectedMethodOnLoadContextAndGetResult(IAssemblyLoadContext loadContext, string methodName, params object[] args) { return (T)loadContext .GetType() .GetMethod(methodName, BindingFlags.Instance | BindingFlags.NonPublic) .Invoke(loadContext, args); } protected IPluginLoadContext GetPluginLoadContext(string pluginAssemblyPath, Action configure = null) { var pluginLoadContext = new PluginLoadContext(pluginAssemblyPath, GetContractType(), "netcoreapp3.1"); configure?.Invoke(pluginLoadContext); return pluginLoadContext; } protected Type GetContractType() { return TestableTypeBuilder.New() .WithName("IMyTestType") .WithNamespace("Test.Type") .Build(); } protected TestContext SetupAssemblyLoadContext() { var nativeUnloader = this.mockRepository.Create(); var pluginDependencyResolver = this.mockRepository.Create(); var assemblyLoadStrategy = this.mockRepository.Create(); var pluginDependencyContext = this.mockRepository.Create(); var resolver = this.mockRepository.Create(); var fileSystemUtility = this.mockRepository.Create(); var runtimeDefaultAssemblyLoadContext = this.mockRepository.Create(); var pluginDependencyContextProvider = this.mockRepository.Create(); var loadContext = new DefaultAssemblyLoadContext( () => nativeUnloader.Object, () => pluginDependencyResolver.Object, () => assemblyLoadStrategy.Object, (s) => resolver.Object, () => fileSystemUtility.Object, () => runtimeDefaultAssemblyLoadContext.Object, () => pluginDependencyContextProvider.Object); return new TestContext(loadContext, nativeUnloader, pluginDependencyResolver, assemblyLoadStrategy, pluginDependencyContext, resolver, fileSystemUtility, runtimeDefaultAssemblyLoadContext, pluginDependencyContextProvider ); } } }