<PackageReference Include="NUnit" Version="3.8.0" />

FrameworkController

FrameworkController provides a facade for use in loading, browsing and running tests without requiring a reference to the NUnit framework. All calls are encapsulated in constructors for this class and its nested classes, which only require the types of the Common Type System as arguments. The controller supports four actions: Load, Explore, Count and Run. They are intended to be called by a driver, which should allow for proper sequencing of calls. Load must be called before any of the other actions. The driver may support other actions, such as reload on run, by combining these calls.
using NUnit.Compatibility; using NUnit.Framework.Interfaces; using NUnit.Framework.Internal; using System; using System.Collections; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.InteropServices; using System.Security; using System.Web.UI; namespace NUnit.Framework.Api { public class FrameworkController : LongLivedMarshalByRefObject { private class ActionCallback : ICallbackEventHandler { private Action<string> _callback; public ActionCallback(Action<string> callback) { _callback = callback; } public string GetCallbackResult() { throw new NotImplementedException(); } public void RaiseCallbackEvent(string report) { if (_callback != null) _callback(report); } } public abstract class FrameworkControllerAction : LongLivedMarshalByRefObject { } public class LoadTestsAction : FrameworkControllerAction { public LoadTestsAction(FrameworkController controller, object handler) { controller.LoadTests((ICallbackEventHandler)handler); } } public class ExploreTestsAction : FrameworkControllerAction { public ExploreTestsAction(FrameworkController controller, string filter, object handler) { controller.ExploreTests((ICallbackEventHandler)handler, filter); } } public class CountTestsAction : FrameworkControllerAction { public CountTestsAction(FrameworkController controller, string filter, object handler) { controller.CountTests((ICallbackEventHandler)handler, filter); } } public class RunTestsAction : FrameworkControllerAction { public RunTestsAction(FrameworkController controller, string filter, object handler) { controller.RunTests((ICallbackEventHandler)handler, filter); } } public class RunAsyncAction : FrameworkControllerAction { public RunAsyncAction(FrameworkController controller, string filter, object handler) { controller.RunAsync((ICallbackEventHandler)handler, filter); } } public class StopRunAction : FrameworkControllerAction { public StopRunAction(FrameworkController controller, bool force, object handler) { controller.StopRun((ICallbackEventHandler)handler, force); } } private const string LOG_FILE_FORMAT = "InternalTrace.{0}.{1}.log"; private Assembly _testAssembly; public ITestAssemblyBuilder Builder { get; set; } public ITestAssemblyRunner Runner { get; set; } public string AssemblyNameOrPath { get; set; } public Assembly Assembly { get; set; } internal IDictionary<string, object> Settings { get; set; } public FrameworkController(string assemblyNameOrPath, string idPrefix, IDictionary settings) { Initialize(assemblyNameOrPath, settings); Builder = new DefaultTestAssemblyBuilder(); Runner = new NUnitTestAssemblyRunner(Builder); Test.IdPrefix = idPrefix; } public FrameworkController(Assembly assembly, string idPrefix, IDictionary settings) : this(assembly.FullName, idPrefix, settings) { _testAssembly = assembly; } public FrameworkController(string assemblyNameOrPath, string idPrefix, IDictionary settings, string runnerType, string builderType) { Initialize(assemblyNameOrPath, settings); Builder = (ITestAssemblyBuilder)Reflect.Construct(Type.GetType(builderType)); Runner = (ITestAssemblyRunner)Reflect.Construct(Type.GetType(runnerType), new object[1] { Builder }); Test.IdPrefix = (idPrefix ?? ""); } public FrameworkController(Assembly assembly, string idPrefix, IDictionary settings, string runnerType, string builderType) : this(assembly.FullName, idPrefix, settings, runnerType, builderType) { _testAssembly = assembly; } [SecuritySafeCritical] private void Initialize(string assemblyPath, IDictionary settings) { AssemblyNameOrPath = assemblyPath; IDictionary<string, object> dictionary = settings as IDictionary<string, object>; Settings = (dictionary ?? settings.Cast<DictionaryEntry>().ToDictionary((DictionaryEntry de) => (string)de.Key, (DictionaryEntry de) => de.Value)); if (Settings.ContainsKey("InternalTraceLevel")) { InternalTraceLevel level = (InternalTraceLevel)Enum.Parse(typeof(InternalTraceLevel), (string)Settings["InternalTraceLevel"], true); if (Settings.ContainsKey("InternalTraceWriter")) InternalTrace.Initialize((TextWriter)Settings["InternalTraceWriter"], level); else { string path = Settings.ContainsKey("WorkDirectory") ? ((string)Settings["WorkDirectory"]) : Directory.GetCurrentDirectory(); string arg = DateTime.Now.ToString("yyyy-dd-M--HH-mm-ss"); string path2 = $"""{arg}""{Path.GetFileName(assemblyPath)}"""; InternalTrace.Initialize(Path.Combine(path, path2), level); } } } public string LoadTests() { if ((object)_testAssembly != null) Runner.Load(_testAssembly, Settings); else Runner.Load(AssemblyNameOrPath, Settings); return Runner.LoadedTest.ToXml(false).OuterXml; } public string ExploreTests(string filter) { if (Runner.LoadedTest == null) throw new InvalidOperationException("The Explore method was called but no test has been loaded"); return Runner.ExploreTests(TestFilter.FromXml(filter)).ToXml(true).OuterXml; } public string RunTests(string filter) { TNode tNode = Runner.Run(new TestProgressReporter(null), TestFilter.FromXml(filter)).ToXml(true); if (Settings != null) InsertSettingsElement(tNode, Settings); InsertEnvironmentElement(tNode); return tNode.OuterXml; } public string RunTests(Action<string> callback, string filter) { ActionCallback handler = new ActionCallback(callback); TNode tNode = Runner.Run(new TestProgressReporter(handler), TestFilter.FromXml(filter)).ToXml(true); if (Settings != null) InsertSettingsElement(tNode, Settings); InsertEnvironmentElement(tNode); return tNode.OuterXml; } private void RunAsync(Action<string> callback, string filter) { ActionCallback handler = new ActionCallback(callback); Runner.RunAsync(new TestProgressReporter(handler), TestFilter.FromXml(filter)); } public void StopRun(bool force) { Runner.StopRun(force); } public int CountTests(string filter) { return Runner.CountTestCases(TestFilter.FromXml(filter)); } private void LoadTests(ICallbackEventHandler handler) { handler.RaiseCallbackEvent(LoadTests()); } private void ExploreTests(ICallbackEventHandler handler, string filter) { if (Runner.LoadedTest == null) throw new InvalidOperationException("The Explore method was called but no test has been loaded"); handler.RaiseCallbackEvent(Runner.ExploreTests(TestFilter.FromXml(filter)).ToXml(true).OuterXml); } private void RunTests(ICallbackEventHandler handler, string filter) { TNode tNode = Runner.Run(new TestProgressReporter(handler), TestFilter.FromXml(filter)).ToXml(true); if (Settings != null) InsertSettingsElement(tNode, Settings); InsertEnvironmentElement(tNode); handler.RaiseCallbackEvent(tNode.OuterXml); } private void RunAsync(ICallbackEventHandler handler, string filter) { Runner.RunAsync(new TestProgressReporter(handler), TestFilter.FromXml(filter)); } private void StopRun(ICallbackEventHandler handler, bool force) { StopRun(force); } private void CountTests(ICallbackEventHandler handler, string filter) { handler.RaiseCallbackEvent(CountTests(filter).ToString()); } public static TNode InsertEnvironmentElement(TNode targetNode) { TNode tNode = new TNode("environment"); targetNode.ChildNodes.Insert(0, tNode); AssemblyName assemblyName = AssemblyHelper.GetAssemblyName(typeof(FrameworkController).GetTypeInfo().get_Assembly()); tNode.AddAttribute("framework-version", assemblyName.Version.ToString()); tNode.AddAttribute("cwd", Directory.GetCurrentDirectory()); tNode.AddAttribute("culture", CultureInfo.CurrentCulture.ToString()); tNode.AddAttribute("uiculture", CultureInfo.CurrentUICulture.ToString()); tNode.AddAttribute("clr-version", RuntimeInformation.FrameworkDescription); tNode.AddAttribute("os-version", RuntimeInformation.OSDescription); tNode.AddAttribute("machine-name", Environment.MachineName); tNode.AddAttribute("os-architecture", RuntimeInformation.ProcessArchitecture.ToString()); return tNode; } public static TNode InsertSettingsElement(TNode targetNode, IDictionary<string, object> settings) { TNode tNode = new TNode("settings"); targetNode.ChildNodes.Insert(0, tNode); foreach (string key in settings.Keys) { AddSetting(tNode, key, settings[key]); } return tNode; } private static void AddSetting(TNode settingsNode, string name, object value) { TNode tNode = new TNode("setting"); tNode.AddAttribute("name", name); if (value is IDictionary) AddDictionaryEntries(tNode, value as IDictionary); else tNode.AddAttribute("value", value.ToString()); settingsNode.ChildNodes.Add(tNode); } private static void AddDictionaryEntries(TNode settingNode, IDictionary entries) { foreach (object key in entries.Keys) { object obj = entries[key]; TNode tNode = new TNode("item"); tNode.AddAttribute("key", key.ToString()); tNode.AddAttribute("value", obj?.ToString() ?? ""); settingNode.ChildNodes.Add(tNode); } } } }