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

NUnitTestAssemblyRunner

Implementation of ITestAssemblyRunner
using NUnit.Framework.Interfaces; using NUnit.Framework.Internal; using NUnit.Framework.Internal.Abstractions; using NUnit.Framework.Internal.Execution; using NUnit.Framework.Internal.Extensions; using System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Security; using System.Threading; namespace NUnit.Framework.Api { [System.Runtime.CompilerServices.NullableContext(1)] [System.Runtime.CompilerServices.Nullable(0)] public class NUnitTestAssemblyRunner : ITestAssemblyRunner { private static readonly Logger Log = InternalTrace.GetLogger("DefaultTestAssemblyRunner"); private readonly ITestAssemblyBuilder _builder; private readonly ManualResetEventSlim _runComplete = new ManualResetEventSlim(); [System.Runtime.CompilerServices.Nullable(2)] private TextWriter _savedOut; [System.Runtime.CompilerServices.Nullable(2)] private TextWriter _savedErr; [System.Runtime.CompilerServices.Nullable(2)] private EventPump _pump; public static int DefaultLevelOfParallelism => Math.Max(Environment.ProcessorCount, 2); [System.Runtime.CompilerServices.Nullable(2)] [field: System.Runtime.CompilerServices.Nullable(2)] public ITest LoadedTest { [System.Runtime.CompilerServices.NullableContext(2)] get; [System.Runtime.CompilerServices.NullableContext(2)] private set; } [System.Runtime.CompilerServices.Nullable(2)] public ITestResult Result { [System.Runtime.CompilerServices.NullableContext(2)] get { return TopLevelWorkItem?.Result; } } public bool IsTestLoaded => LoadedTest != null; public bool IsTestRunning { get { if (TopLevelWorkItem != null) return TopLevelWorkItem.State == WorkItemState.Running; return false; } } public bool IsTestComplete { get { if (TopLevelWorkItem != null) return TopLevelWorkItem.State == WorkItemState.Complete; return false; } } private IDictionary<string, object> Settings { get; set; } [System.Runtime.CompilerServices.Nullable(2)] [field: System.Runtime.CompilerServices.Nullable(2)] private WorkItem TopLevelWorkItem { [System.Runtime.CompilerServices.NullableContext(2)] get; [System.Runtime.CompilerServices.NullableContext(2)] set; } [System.Runtime.CompilerServices.Nullable(2)] [field: System.Runtime.CompilerServices.Nullable(2)] private TestExecutionContext Context { [System.Runtime.CompilerServices.NullableContext(2)] get; [System.Runtime.CompilerServices.NullableContext(2)] set; } public NUnitTestAssemblyRunner(ITestAssemblyBuilder builder) { _builder = builder; } public ITest Load(string assemblyNameOrPath, IDictionary<string, object> settings) { Settings = settings; if (settings.TryGetValue("RandomSeed", out object value)) Randomizer.InitialSeed = (int)value; LoadedTest = WrapInNUnitCallContext(() => _builder.Build(assemblyNameOrPath, settings)); return LoadedTest; } public ITest Load(Assembly assembly, IDictionary<string, object> settings) { Settings = settings; if (settings.TryGetValue("RandomSeed", out object value)) Randomizer.InitialSeed = (int)value; LoadedTest = WrapInNUnitCallContext(() => _builder.Build(assembly, settings)); return LoadedTest; } public int CountTestCases(ITestFilter filter) { if (LoadedTest == null) throw new InvalidOperationException("Tests must be loaded before counting test cases."); return CountTestCases(LoadedTest, filter); } public ITest ExploreTests(ITestFilter filter) { if (LoadedTest == null) throw new InvalidOperationException("Tests must be loaded before exploring them."); if (filter == TestFilter.Empty) return LoadedTest; return new TestAssembly((TestAssembly)LoadedTest, filter); } public ITestResult Run(ITestListener listener, ITestFilter filter) { RunAsync(listener, filter); WaitForCompletion(-1); return Result; } public void RunAsync(ITestListener listener, ITestFilter filter) { Log.Info("Running tests"); if (LoadedTest == null) throw new InvalidOperationException("Tests must be loaded before running them."); _runComplete.Reset(); TestExecutionContext context = CreateTestExecutionContext(LoadedTest, listener); TopLevelWorkItem = WorkItemBuilder.CreateWorkItem(LoadedTest, filter, new DebuggerProxy(), true, true); if (TopLevelWorkItem == null) throw new InvalidOperationException("Loaded test didn't result in a WorkItem"); TopLevelWorkItem.InitializeContext(context); TopLevelWorkItem.Completed += OnRunCompleted; Context = context; WrapInNUnitCallContext(delegate { StartRun(context, TopLevelWorkItem, listener); }); } public bool WaitForCompletion(int timeout) { return _runComplete.Wait(timeout); } public void StopRun(bool force) { if (IsTestRunning && Context != null) { Context.ExecutionStatus = ((!force) ? TestExecutionStatus.StopRequested : TestExecutionStatus.AbortRequested); Context.Dispatcher.CancelRun(force); } } private void StartRun(TestExecutionContext context, WorkItem topLevelWorkItem, ITestListener listener) { _savedOut = Console.Out; _savedErr = Console.Error; Console.SetOut(new TextCapture(Console.Out)); Console.SetError(new EventListenerTextWriter("Error", Console.Error)); if (!Settings.TryGetValue("SynchronousEvents", out object value) || !(bool)value) { QueuingEventListener queuingEventListener = (QueuingEventListener)(context.Listener = new QueuingEventListener()); _pump = new EventPump(listener, queuingEventListener.Events); _pump.Start(); } object value2; if (!Debugger.IsAttached && Settings.TryGetValue("DebugTests", out value2) && (bool)value2) try { Debugger.Launch(); } catch (SecurityException) { topLevelWorkItem.MarkNotRunnable("System.Security.Permissions.UIPermission must be granted in order to launch the debugger."); return; } catch (NotImplementedException) { topLevelWorkItem.MarkNotRunnable("This platform does not support launching the debugger."); return; } context.Dispatcher.Start(topLevelWorkItem); } private TestExecutionContext CreateTestExecutionContext(ITest loadedTest, ITestListener listener) { TestExecutionContext testExecutionContext = new TestExecutionContext(); if (Settings.TryGetValue("DefaultTimeout", out object value)) testExecutionContext.TestCaseTimeout = (int)value; if (Settings.TryGetValue("DefaultCulture", out object value2)) testExecutionContext.CurrentCulture = new CultureInfo((string)value2, false); if (Settings.TryGetValue("DefaultUICulture", out object value3)) testExecutionContext.CurrentUICulture = new CultureInfo((string)value3, false); if (Settings.TryGetValue("StopOnError", out object value4)) testExecutionContext.StopOnError = (bool)value4; if (Settings.TryGetValue("ThrowOnEachFailureUnderDebugger", out object value5)) testExecutionContext.ThrowOnEachFailureUnderDebugger = (bool)value5; testExecutionContext.Listener = listener; int levelOfParallelism = GetLevelOfParallelism(loadedTest); if (Settings.TryGetValue("RunOnMainThread", out object value6) && (bool)value6) testExecutionContext.Dispatcher = new MainThreadWorkItemDispatcher(); else if (levelOfParallelism > 0) { testExecutionContext.Dispatcher = new ParallelWorkItemDispatcher(levelOfParallelism); } else { testExecutionContext.Dispatcher = new SimpleWorkItemDispatcher(); } return testExecutionContext; } private void OnRunCompleted([System.Runtime.CompilerServices.Nullable(2)] object sender, EventArgs e) { if (_pump != null) _pump.Dispose(); if (_savedOut != null) Console.SetOut(_savedOut); if (_savedErr != null) Console.SetError(_savedErr); _runComplete.Set(); } private static int CountTestCases(ITest test, ITestFilter filter) { if (!test.IsSuite) return filter.Pass(test) ? 1 : 0; int num = 0; IList<ITest> tests = test.Tests; for (int i = 0; i < tests.Count; i++) { num += CountTestCases(tests[i], filter); } return num; } private int GetLevelOfParallelism(ITest loadedTest) { if (!Settings.TryGetValue("NumberOfTestWorkers", out object value)) return loadedTest.Properties.TryGet("LevelOfParallelism", DefaultLevelOfParallelism); return (int)value; } protected void WrapInNUnitCallContext(Action action) { action(); } protected T WrapInNUnitCallContext<[System.Runtime.CompilerServices.Nullable(2)] T>(Func<T> function) { return function(); } } }