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

WorkItem

public abstract class WorkItem : IDisposable
A WorkItem may be an individual test case, a fixture or a higher level grouping of tests. All WorkItems inherit from the abstract WorkItem class, which uses the template pattern to allow derived classes to perform work in whatever way is needed. A WorkItem is created with a particular TestExecutionContext and is responsible for re-establishing that context in the current thread before it begins or resumes execution.
using NUnit.Framework.Interfaces; using NUnit.Framework.Internal.Commands; using NUnit.Framework.Internal.Extensions; using System; using System.Collections.Generic; using System.Diagnostics; using System.Runtime.CompilerServices; using System.Threading; namespace NUnit.Framework.Internal.Execution { [System.Runtime.CompilerServices.NullableContext(1)] [System.Runtime.CompilerServices.Nullable(0)] public abstract class WorkItem : IDisposable { private static readonly Logger Log = InternalTrace.GetLogger("WorkItem"); private ParallelExecutionStrategy? _executionStrategy; private readonly ManualResetEventSlim _completionEvent = new ManualResetEventSlim(); [System.Runtime.CompilerServices.Nullable(2)] private Thread _thread; public WorkItemState State { get; set; } public Test Test { get; } public virtual string Name => Test.Name; public ITestFilter Filter { get; } public TestExecutionContext Context { get; set; } [System.Runtime.CompilerServices.Nullable(2)] [field: System.Runtime.CompilerServices.Nullable(2)] public TestWorker TestWorker { [System.Runtime.CompilerServices.NullableContext(2)] get; [System.Runtime.CompilerServices.NullableContext(2)] internal set; } public virtual ParallelExecutionStrategy ExecutionStrategy { get { if (!_executionStrategy.HasValue) _executionStrategy = GetExecutionStrategy(); return _executionStrategy.Value; } } public virtual bool IsolateChildTests { get; } public TestResult Result { get; set; } public ParallelScope ParallelScope { get; } internal ApartmentState TargetApartment { get; set; } private ApartmentState CurrentApartment { get; set; } [System.Runtime.CompilerServices.Nullable(2)] [method: System.Runtime.CompilerServices.NullableContext(2)] [field: System.Runtime.CompilerServices.Nullable(2)] public event EventHandler Completed; public WorkItem(Test test, ITestFilter filter) { Test = test; Filter = filter; Result = test.MakeTestResult(); State = WorkItemState.Ready; ParallelScope = Test.Properties.TryGet("ParallelScope", ParallelScope.Default); TargetApartment = GetTargetApartment(Test); State = WorkItemState.Ready; Context = null; } public WorkItem(WorkItem wrappedItem) { Test = wrappedItem.Test; Result = wrappedItem.Result; Filter = wrappedItem.Filter; Context = wrappedItem.Context; ParallelScope = wrappedItem.ParallelScope; TestWorker = wrappedItem.TestWorker; TargetApartment = wrappedItem.TargetApartment; State = WorkItemState.Ready; } public void InitializeContext(TestExecutionContext context) { Guard.OperationValid(Context == null, "The context has already been initialized"); Context = context; } public virtual void Execute() { Guard.OperationValid(Context != null, "Context must be set by InitializeContext"); CurrentApartment = Thread.CurrentThread.GetApartmentState(); ApartmentState apartmentState = (TargetApartment == ApartmentState.Unknown) ? CurrentApartment : TargetApartment; bool flag = apartmentState != CurrentApartment; if (Test.RequiresThread | flag) { if (Context.IsSingleThreaded) { string message = Test.RequiresThread ? "RequiresThreadAttribute may not be specified on a test within a single-SingleThreadedAttribute fixture." : "Tests in a single-threaded fixture may not specify a different apartment"; Log.Error(message); Result.SetResult(ResultState.NotRunnable, message); WorkItemComplete(); } else { Log.Debug("Running on separate thread because {0} is specified.", Test.RequiresThread ? "RequiresThread" : "different Apartment"); RunOnSeparateThread(apartmentState); } } else RunOnCurrentThread(); } public void WaitForCompletion() { _completionEvent.Wait(); } public void MarkNotRunnable(string reason) { Result.SetResult(ResultState.NotRunnable, reason); WorkItemComplete(); } public virtual void Cancel(bool force) { if (Context != null) Context.ExecutionStatus = ((!force) ? TestExecutionStatus.StopRequested : TestExecutionStatus.AbortRequested); } public void Dispose() { _completionEvent?.Dispose(); } protected abstract void PerformWork(); protected void WorkItemComplete() { State = WorkItemState.Complete; Result.StartTime = Context.StartTime; Result.EndTime = DateTime.UtcNow; Result.Duration = Context.Duration; Result.AssertCount += Context.AssertCount; Context.Listener.TestFinished(Result); this.Completed?.Invoke(this, EventArgs.Empty); _completionEvent.Set(); Context.TestObject = null; Test.Fixture = null; } protected List<SetUpTearDownItem> BuildSetUpTearDownList(IMethodInfo[] setUpMethods, IMethodInfo[] tearDownMethods, [System.Runtime.CompilerServices.Nullable(2)] IMethodValidator methodValidator = null) { Guard.ArgumentNotNull(setUpMethods, "setUpMethods"); Guard.ArgumentNotNull(tearDownMethods, "tearDownMethods"); List<SetUpTearDownItem> list = new List<SetUpTearDownItem>(); Type type = Test.TypeInfo?.Type; if ((object)type == null) return list; while ((object)type != null && type != typeof(object)) { SetUpTearDownItem setUpTearDownItem = BuildNode(type, setUpMethods, tearDownMethods, methodValidator); if (setUpTearDownItem.HasMethods) list.Add(setUpTearDownItem); type = type.BaseType; } return list; } private static SetUpTearDownItem BuildNode(Type fixtureType, IList<IMethodInfo> setUpMethods, IList<IMethodInfo> tearDownMethods, [System.Runtime.CompilerServices.Nullable(2)] IMethodValidator methodValidator) { List<IMethodInfo> setUpMethods2 = SelectMethodsByDeclaringType(fixtureType, setUpMethods); List<IMethodInfo> tearDownMethods2 = SelectMethodsByDeclaringType(fixtureType, tearDownMethods); return new SetUpTearDownItem(setUpMethods2, tearDownMethods2, methodValidator); } private static List<IMethodInfo> SelectMethodsByDeclaringType(Type type, IList<IMethodInfo> methods) { List<IMethodInfo> list = new List<IMethodInfo>(); foreach (IMethodInfo method in methods) { if (method.TypeInfo.Type == type) list.Add(method); } return list; } protected void ChangeResult(ResultState resultState, string message) { Log.Debug("Changing result from {0} to {1}", Result.ResultState, resultState); Result.SetResult(resultState, message); } private void RunOnSeparateThread(ApartmentState apartment) { _thread = new Thread((ThreadStart)delegate { Thread.CurrentThread.CurrentCulture = Context.CurrentCulture; Thread.CurrentThread.CurrentUICulture = Context.CurrentUICulture; RunOnCurrentThread(); }); if (OperatingSystem.IsWindows()) { _thread.SetApartmentState(apartment); _thread.Start(); _thread.Join(); } else { Log.Error("Apartment state cannot be set on this platform."); Result.SetResult(ResultState.Skipped, "Apartment state cannot be set on this platform."); WorkItemComplete(); } } private void RunOnCurrentThread() { Context.CurrentTest = Test; Context.CurrentResult = Result; Context.Listener.TestStarted(Test); Context.StartTime = DateTime.UtcNow; Context.StartTicks = Stopwatch.GetTimestamp(); Context.TestWorker = TestWorker; Context.EstablishExecutionEnvironment(); State = WorkItemState.Running; PerformWork(); } private ParallelExecutionStrategy GetExecutionStrategy() { if (Test.TypeInfo == null) return ParallelExecutionStrategy.Direct; if (Context.IsSingleThreaded) return ParallelExecutionStrategy.Direct; if (ParallelScope.HasFlag(ParallelScope.None)) return ParallelExecutionStrategy.NonParallel; if (ParallelScope.HasFlag(ParallelScope.Self)) return ParallelExecutionStrategy.Parallel; if (Context.ParallelScope.HasFlag(ParallelScope.Children) || (Test is TestFixture && Context.ParallelScope.HasFlag(ParallelScope.Fixtures))) return ParallelExecutionStrategy.Parallel; if (!(this is SimpleWorkItem)) return ParallelExecutionStrategy.NonParallel; return ParallelExecutionStrategy.Direct; } private static ApartmentState GetTargetApartment(ITest test) { ApartmentState apartmentState = test.Properties.TryGet("ApartmentState", ApartmentState.Unknown); if (apartmentState == ApartmentState.Unknown && test.Parent != null) return GetTargetApartment(test.Parent); return apartmentState; } } }