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.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Web.UI;
namespace NUnit.Framework.Api
{
[NullableContext(1)]
[Nullable(0)]
public class FrameworkController : LongLivedMarshalByRefObject
{
[Nullable(0)]
private class ActionCallback : ICallbackEventHandler
{
private readonly 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);
}
}
[NullableContext(0)]
public abstract class FrameworkControllerAction : LongLivedMarshalByRefObject
{
}
[NullableContext(0)]
public class LoadTestsAction : FrameworkControllerAction
{
[NullableContext(1)]
public LoadTestsAction(FrameworkController controller, object handler)
{
controller.LoadTests((ICallbackEventHandler)handler);
}
}
[NullableContext(0)]
public class ExploreTestsAction : FrameworkControllerAction
{
[NullableContext(1)]
public ExploreTestsAction(FrameworkController controller, [Nullable(2)] string filter, object handler)
{
controller.ExploreTests((ICallbackEventHandler)handler, filter);
}
}
[NullableContext(0)]
public class CountTestsAction : FrameworkControllerAction
{
[NullableContext(1)]
public CountTestsAction(FrameworkController controller, [Nullable(2)] string filter, object handler)
{
controller.CountTests((ICallbackEventHandler)handler, filter);
}
}
[NullableContext(0)]
public class RunTestsAction : FrameworkControllerAction
{
[NullableContext(1)]
public RunTestsAction(FrameworkController controller, [Nullable(2)] string filter, object handler)
{
controller.RunTests((ICallbackEventHandler)handler, filter);
}
}
[NullableContext(0)]
public class RunAsyncAction : FrameworkControllerAction
{
[NullableContext(1)]
public RunAsyncAction(FrameworkController controller, [Nullable(2)] string filter, object handler)
{
controller.RunAsync((ICallbackEventHandler)handler, filter);
}
}
[NullableContext(0)]
public class StopRunAction : FrameworkControllerAction
{
[NullableContext(1)]
public StopRunAction(FrameworkController controller, bool force, object handler)
{
controller.StopRun((ICallbackEventHandler)handler, force);
}
}
private const string LOG_FILE_FORMAT = "InternalTrace.{0}.{1}.log";
[Nullable(2)]
private readonly Assembly _testAssembly;
public ITestAssemblyBuilder Builder { get; }
public ITestAssemblyRunner Runner { get; }
public string AssemblyNameOrPath { get; set; }
internal IDictionary<string, object> Settings { get; set; }
public FrameworkController(string assemblyNameOrPath, [Nullable(2)] string idPrefix, IDictionary settings)
{
Initialize(assemblyNameOrPath, settings);
Builder = new DefaultTestAssemblyBuilder();
Runner = new NUnitTestAssemblyRunner(Builder);
Test.IdPrefix = (idPrefix ?? string.Empty);
}
public FrameworkController(Assembly assembly, string idPrefix, IDictionary settings)
: this(assembly.FullName, idPrefix, settings)
{
_testAssembly = assembly;
}
public FrameworkController(string assemblyNameOrPath, [Nullable(2)] string idPrefix, IDictionary settings, string runnerType, string builderType)
{
Initialize(assemblyNameOrPath, settings);
Builder = (ITestAssemblyBuilder)Reflect.Construct(Type.GetType(builderType, true));
Runner = (ITestAssemblyRunner)Reflect.Construct(Type.GetType(runnerType, true), new object[1] {
Builder
});
Test.IdPrefix = (idPrefix ?? string.Empty);
}
public FrameworkController(Assembly assembly, string idPrefix, IDictionary settings, string runnerType, string builderType)
: this(assembly.FullName, idPrefix, settings, runnerType, builderType)
{
_testAssembly = assembly;
}
[MemberNotNull(new string[] {
"AssemblyNameOrPath",
"Settings"
})]
private void Initialize(string assemblyNameOrPath, IDictionary settings)
{
AssemblyNameOrPath = assemblyNameOrPath;
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.TryGetValue("InternalTraceLevel", out object value)) {
InternalTraceLevel level = (InternalTraceLevel)Enum.Parse(typeof(InternalTraceLevel), (string)value, true);
if (Settings.TryGetValue("InternalTraceWriter", out object value2))
InternalTrace.Initialize((TextWriter)value2, level);
else {
object value3;
string path = Settings.TryGetValue("WorkDirectory", out value3) ? ((string)value3) : Directory.GetCurrentDirectory();
using (Process process = Process.GetCurrentProcess()) {
int id = process.Id;
string path2 = $"""{id}""{Path.GetFileName(assemblyNameOrPath)}""";
InternalTrace.Initialize(Path.Combine(path, path2), level);
}
}
}
}
public string LoadTests()
{
ITest test = ((object)_testAssembly == null) ? Runner.Load(AssemblyNameOrPath, Settings) : Runner.Load(_testAssembly, Settings);
return test.ToXml(false).OuterXml;
}
public string ExploreTests([Nullable(2)] string filter)
{
TNode targetNode = Runner.ExploreTests(TestFilter.FromXml(filter)).ToXml(true);
return InsertChildElements(targetNode).OuterXml;
}
public string RunTests([Nullable(2)] string filter)
{
TNode targetNode = Runner.Run(TestListener.NULL, TestFilter.FromXml(filter)).ToXml(true);
return InsertChildElements(targetNode).OuterXml;
}
public string RunTests(Action<string> callback, string filter)
{
ActionCallback handler = new ActionCallback(callback);
TNode targetNode = Runner.Run(new TestProgressReporter(handler), TestFilter.FromXml(filter)).ToXml(true);
return InsertChildElements(targetNode).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);
}
[NullableContext(2)]
public int CountTests(string filter)
{
return Runner.CountTestCases(TestFilter.FromXml(filter));
}
private void LoadTests(ICallbackEventHandler handler)
{
handler.RaiseCallbackEvent(LoadTests());
}
private void ExploreTests(ICallbackEventHandler handler, [Nullable(2)] string filter)
{
handler.RaiseCallbackEvent(ExploreTests(filter));
}
private void RunTests(ICallbackEventHandler handler, [Nullable(2)] string filter)
{
TNode tNode = Runner.Run(new TestProgressReporter(handler), TestFilter.FromXml(filter)).ToXml(true);
InsertSettingsElement(tNode, Settings);
InsertEnvironmentElement(tNode);
handler.RaiseCallbackEvent(tNode.OuterXml);
}
private void RunAsync(ICallbackEventHandler handler, [Nullable(2)] string filter)
{
Runner.RunAsync(new TestProgressReporter(handler), TestFilter.FromXml(filter));
}
private void StopRun(ICallbackEventHandler handler, bool force)
{
StopRun(force);
}
private void CountTests(ICallbackEventHandler handler, [Nullable(2)] string filter)
{
handler.RaiseCallbackEvent(CountTests(filter).ToString());
}
private TNode InsertChildElements(TNode targetNode)
{
InsertSettingsElement(targetNode, Settings);
InsertEnvironmentElement(targetNode);
return targetNode;
}
public static TNode InsertEnvironmentElement(TNode targetNode)
{
TNode tNode = new TNode("environment");
targetNode.InsertChildNode(0, tNode);
tNode.AddAttribute("framework-version", typeof(FrameworkController).Assembly.GetName().Version?.ToString() ?? "Unknown");
tNode.AddAttribute("clr-version", Environment.Version.ToString());
tNode.AddAttribute("os-version", OSPlatform.OSDescription);
tNode.AddAttribute("platform", Environment.OSVersion.Platform.ToString());
tNode.AddAttribute("cwd", Directory.GetCurrentDirectory());
tNode.AddAttribute("machine-name", Environment.MachineName);
tNode.AddAttribute("user", Environment.UserName);
tNode.AddAttribute("user-domain", Environment.UserDomainName);
tNode.AddAttribute("culture", CultureInfo.CurrentCulture.ToString());
tNode.AddAttribute("uiculture", CultureInfo.CurrentUICulture.ToString());
tNode.AddAttribute("os-architecture", GetProcessorArchitecture());
return tNode;
}
private static string GetProcessorArchitecture()
{
if (IntPtr.Size != 8)
return "x86";
return "x64";
}
public static TNode InsertSettingsElement(TNode targetNode, IDictionary<string, object> settings)
{
TNode tNode = new TNode("settings");
targetNode.InsertChildNode(0, tNode);
foreach (string key in settings.Keys) {
AddSetting(tNode, key, settings[key]);
}
if (!settings.ContainsKey("NumberOfTestWorkers"))
AddSetting(tNode, "NumberOfTestWorkers", NUnitTestAssemblyRunner.DefaultLevelOfParallelism);
return tNode;
}
private static void AddSetting(TNode settingsNode, string name, object value)
{
TNode tNode = new TNode("setting");
tNode.AddAttribute("name", name);
IDictionary dictionary = value as IDictionary;
if (dictionary != null) {
AddDictionaryEntries(tNode, dictionary);
AddBackwardsCompatibleDictionaryEntries(tNode, dictionary);
} else if (value != null) {
tNode.AddAttribute("value", value.ToString());
}
settingsNode.AddChildNode(tNode);
}
private static void AddBackwardsCompatibleDictionaryEntries(TNode settingsNode, IDictionary entries)
{
List<string> list = new List<string>(entries.Count);
foreach (object key in entries.Keys) {
List<string> list2 = list;
DefaultInterpolatedStringHandler defaultInterpolatedStringHandler = new DefaultInterpolatedStringHandler(4, 2);
defaultInterpolatedStringHandler.AppendLiteral("[");
defaultInterpolatedStringHandler.AppendFormatted<object>(key);
defaultInterpolatedStringHandler.AppendLiteral(", ");
defaultInterpolatedStringHandler.AppendFormatted<object>(entries[key]);
defaultInterpolatedStringHandler.AppendLiteral("]");
list2.Add(defaultInterpolatedStringHandler.ToStringAndClear());
}
settingsNode.AddAttribute("value", string.Join(", ", list.ToArray()));
}
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() ?? string.Empty);
settingNode.AddChildNode(tNode);
}
}
}
}