From c995732f352be6d7f79d1c3f14131bc52a61e654 Mon Sep 17 00:00:00 2001 From: benbierens Date: Tue, 12 Sep 2023 11:25:04 +0200 Subject: [PATCH] Fun with assembly loading --- CodexPlugin/{Plugin.cs => CodexPlugin.cs} | 4 +-- CodexPlugin/DistTestExtensions.cs | 30 ------------------- CodexPlugin/PluginInterfaceExtensions.cs | 33 +++++++++++++++++++++ DistTestCore/DistTest.cs | 7 ++++- DistTestCore/PluginInterface.cs | 7 +++++ DistTestCore/PluginManager.cs | 35 ++++++++++++++++++++++- DistTestCore/TestLifecycle.cs | 5 ++++ Logging/BaseTestLog.cs | 19 ++++++++++++ Logging/FixtureLog.cs | 4 +-- Logging/TestLog.cs | 10 +------ 10 files changed, 108 insertions(+), 46 deletions(-) rename CodexPlugin/{Plugin.cs => CodexPlugin.cs} (93%) delete mode 100644 CodexPlugin/DistTestExtensions.cs create mode 100644 CodexPlugin/PluginInterfaceExtensions.cs create mode 100644 DistTestCore/PluginInterface.cs create mode 100644 Logging/BaseTestLog.cs diff --git a/CodexPlugin/Plugin.cs b/CodexPlugin/CodexPlugin.cs similarity index 93% rename from CodexPlugin/Plugin.cs rename to CodexPlugin/CodexPlugin.cs index e7c72a0..a47f1a5 100644 --- a/CodexPlugin/Plugin.cs +++ b/CodexPlugin/CodexPlugin.cs @@ -4,7 +4,7 @@ using Logging; namespace CodexPlugin { - public class Plugin : IProjectPlugin + public class CodexPlugin : IProjectPlugin { private CodexStarter codexStarter = null!; @@ -18,8 +18,6 @@ namespace CodexPlugin public void Initialize(IPluginTools tools) { codexStarter = new CodexStarter(tools); - - DistTestExtensions.Plugin = this; } public void Finalize(ILog log) diff --git a/CodexPlugin/DistTestExtensions.cs b/CodexPlugin/DistTestExtensions.cs deleted file mode 100644 index de0362a..0000000 --- a/CodexPlugin/DistTestExtensions.cs +++ /dev/null @@ -1,30 +0,0 @@ -using DistTestCore; -using KubernetesWorkflow; - -namespace CodexPlugin -{ - public static class DistTestExtensions - { - public static Plugin Plugin { get; internal set; } = null!; - - public static RunningContainers[] StartCodexNodes(this DistTest distTest, int number, Action setup) - { - return Plugin.StartCodexNodes(number, setup); - } - - public static ICodexNodeGroup WrapCodexContainers(this DistTest distTest, RunningContainers[] containers) - { - return Plugin.WrapCodexContainers(containers); - } - - public static IOnlineCodexNode SetupCodexNode(this DistTest distTest, Action setup) - { - return Plugin.SetupCodexNode(setup); - } - - public static ICodexNodeGroup SetupCodexNodes(this DistTest distTest, int number) - { - return Plugin.SetupCodexNodes(number); - } - } -} diff --git a/CodexPlugin/PluginInterfaceExtensions.cs b/CodexPlugin/PluginInterfaceExtensions.cs new file mode 100644 index 0000000..8fa21f2 --- /dev/null +++ b/CodexPlugin/PluginInterfaceExtensions.cs @@ -0,0 +1,33 @@ +using DistTestCore; +using KubernetesWorkflow; + +namespace CodexPlugin +{ + public static class PluginInterfaceExtensions + { + public static RunningContainers[] StartCodexNodes(this PluginInterface pluginInterface, int number, Action setup) + { + return Plugin(pluginInterface).StartCodexNodes(number, setup); + } + + public static ICodexNodeGroup WrapCodexContainers(this PluginInterface pluginInterface, RunningContainers[] containers) + { + return Plugin(pluginInterface).WrapCodexContainers(containers); + } + + public static IOnlineCodexNode SetupCodexNode(this PluginInterface pluginInterface, Action setup) + { + return Plugin(pluginInterface).SetupCodexNode(setup); + } + + public static ICodexNodeGroup SetupCodexNodes(this PluginInterface pluginInterface, int number) + { + return Plugin(pluginInterface).SetupCodexNodes(number); + } + + private static CodexPlugin Plugin(PluginInterface pluginInterface) + { + return pluginInterface.GetPlugin(); + } + } +} diff --git a/DistTestCore/DistTest.cs b/DistTestCore/DistTest.cs index d445944..d5020e2 100644 --- a/DistTestCore/DistTest.cs +++ b/DistTestCore/DistTest.cs @@ -9,7 +9,7 @@ using Utils; namespace DistTestCore { [Parallelizable(ParallelScope.All)] - public abstract class DistTest + public abstract class DistTest : PluginInterface { private const string TestsType = "dist-tests"; private readonly Configuration configuration = new Configuration(); @@ -153,6 +153,11 @@ namespace DistTestCore // return Get().CodexStarter.RunningGroups.SelectMany(g => g.Nodes); //} + public override T GetPlugin() + { + return Get().GetPlugin(); + } + private void AnnouncePlugins(FixtureLog fixtureLog) { PluginManager.AnnouncePlugins(fixtureLog); diff --git a/DistTestCore/PluginInterface.cs b/DistTestCore/PluginInterface.cs new file mode 100644 index 0000000..72b0da3 --- /dev/null +++ b/DistTestCore/PluginInterface.cs @@ -0,0 +1,7 @@ +namespace DistTestCore +{ + public abstract class PluginInterface + { + public abstract T GetPlugin() where T : IProjectPlugin; + } +} diff --git a/DistTestCore/PluginManager.cs b/DistTestCore/PluginManager.cs index 27f9bf7..b4bb804 100644 --- a/DistTestCore/PluginManager.cs +++ b/DistTestCore/PluginManager.cs @@ -1,6 +1,7 @@ using FileUtils; using KubernetesWorkflow; using Logging; +using System.Reflection; using Utils; namespace DistTestCore @@ -34,6 +35,11 @@ namespace DistTestCore { foreach (var plugin in projectPlugins) plugin.Finalize(log); } + + public T GetPlugin() where T : IProjectPlugin + { + return (T)projectPlugins.Single(p => p.GetType() == typeof(T)); + } } public static class PluginFinder @@ -45,10 +51,37 @@ namespace DistTestCore if (pluginTypes != null) return pluginTypes; // Reflection can be costly. Do this only once. + FindAndLoadPluginAssemblies(); + var assemblies = AppDomain.CurrentDomain.GetAssemblies(); - pluginTypes = assemblies.SelectMany(a => a.GetTypes().Where(t => typeof(IProjectPlugin).IsAssignableFrom(t))).ToArray(); + pluginTypes = assemblies.SelectMany(a => a.GetTypes().Where(t => + typeof(IProjectPlugin).IsAssignableFrom(t) && + !t.IsAbstract) + ).ToArray(); + return pluginTypes; } + + private static void FindAndLoadPluginAssemblies() + { + var files = Directory.GetFiles("."); + foreach (var file in files) + { + var f = file.ToLowerInvariant(); + if (f.Contains("plugin") && f.EndsWith("dll")) + { + var name = Path.GetFileNameWithoutExtension(file); + try + { + Assembly.Load(name); + } + catch (Exception ex) + { + throw new Exception($"Failed to load plugin from file '{name}'.", ex); + } + } + } + } } public interface IProjectPlugin diff --git a/DistTestCore/TestLifecycle.cs b/DistTestCore/TestLifecycle.cs index 851e1f9..a13dee2 100644 --- a/DistTestCore/TestLifecycle.cs +++ b/DistTestCore/TestLifecycle.cs @@ -32,6 +32,11 @@ namespace DistTestCore public string TestNamespace { get; } public IFileManager FileManager { get; } + public T GetPlugin() where T : IProjectPlugin + { + return pluginManager.GetPlugin(); + } + public Http CreateHttp(Address address, string baseUrl, Action onClientCreated, string? logAlias = null) { return new Http(Log, TimeSet, address, baseUrl, onClientCreated, logAlias); diff --git a/Logging/BaseTestLog.cs b/Logging/BaseTestLog.cs new file mode 100644 index 0000000..99f1eb1 --- /dev/null +++ b/Logging/BaseTestLog.cs @@ -0,0 +1,19 @@ +namespace Logging +{ + public abstract class BaseTestLog : BaseLog + { + private bool hasFailed; + + public BaseTestLog(bool debug) + : base(debug) + { + } + + public void MarkAsFailed() + { + if (hasFailed) return; + hasFailed = true; + LogFile.ConcatToFilename("_FAILED"); + } + } +} diff --git a/Logging/FixtureLog.cs b/Logging/FixtureLog.cs index 2a11fbd..306b40a 100644 --- a/Logging/FixtureLog.cs +++ b/Logging/FixtureLog.cs @@ -1,12 +1,12 @@ namespace Logging { - public class FixtureLog : TestLog + public class FixtureLog : BaseTestLog { private readonly string fullName; private readonly LogConfig config; public FixtureLog(LogConfig config, DateTime start, string name = "") - : base(config.LogRoot, config.DebugEnabled) + : base(config.DebugEnabled) { fullName = NameUtils.GetFixtureFullName(config, start, name); this.config = config; diff --git a/Logging/TestLog.cs b/Logging/TestLog.cs index 4279d10..217a755 100644 --- a/Logging/TestLog.cs +++ b/Logging/TestLog.cs @@ -1,10 +1,9 @@ namespace Logging { - public class TestLog : BaseLog + public class TestLog : BaseTestLog { private readonly string methodName; private readonly string fullName; - private bool hasFailed; public TestLog(string folder, bool debug, string name = "") : base(debug) @@ -15,13 +14,6 @@ Log($"*** Begin: {methodName}"); } - public void MarkAsFailed() - { - if (hasFailed) return; - hasFailed = true; - LogFile.ConcatToFilename("_FAILED"); - } - protected override string GetFullName() { return fullName;