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

CompositeWorkItem

public class CompositeWorkItem : WorkItem
A CompositeWorkItem represents a test suite and encapsulates the execution of the suite as well as all its child tests.
using NUnit.Framework.Interfaces; using NUnit.Framework.Internal.Commands; using NUnit.Framework.Internal.Extensions; using System; using System.Collections.Generic; using System.Runtime.CompilerServices; using System.Threading; namespace NUnit.Framework.Internal.Execution { [NullableContext(1)] [Nullable(0)] public class CompositeWorkItem : WorkItem { [Nullable(0)] public class OneTimeTearDownWorkItem : WorkItem { private readonly CompositeWorkItem _originalWorkItem; private readonly object _teardownLock = new object(); public override string Name => base.Name + " OneTimeTearDown"; public override ParallelExecutionStrategy ExecutionStrategy => _originalWorkItem.ExecutionStrategy; public OneTimeTearDownWorkItem(CompositeWorkItem originalItem) : base(originalItem) { _originalWorkItem = originalItem; } public override void Execute() { lock (_teardownLock) { if (base.Test.TestType == "Theory" && base.Result.ResultState == ResultState.Success && base.Result.PassCount == 0) base.Result.SetResult(ResultState.Failure, "No test cases were provided"); if (base.Context.ExecutionStatus != TestExecutionStatus.AbortRequested) _originalWorkItem.PerformOneTimeTearDown(); foreach (ITestResult child in base.Result.Children) { if (child.ResultState == ResultState.Cancelled) { base.Result.SetResult(ResultState.Cancelled, "Cancelled by user"); break; } } _originalWorkItem.WorkItemComplete(); } } protected override void PerformWork() { } internal void WorkItemCancelled() { base.Result.SetResult(ResultState.Cancelled, "Test run cancelled by user"); _originalWorkItem.WorkItemComplete(); } } private readonly TestSuite _suite; private readonly TestSuiteResult _suiteResult; [Nullable(2)] private TestCommand _setupCommand; [Nullable(2)] private TestCommand _teardownCommand; [Nullable(2)] private CountdownEvent _childTestCountdown; private readonly object _childCompletionLock = new object(); private readonly object _cancelLock = new object(); public List<WorkItem> Children { get; } = new List<WorkItem>(); public override bool IsolateChildTests { get { if (ExecutionStrategy == ParallelExecutionStrategy.NonParallel) return base.Context.Dispatcher.LevelOfParallelism > 0; return false; } } public CompositeWorkItem(TestSuite suite, ITestFilter childFilter) : base(suite, childFilter) { _suite = suite; _suiteResult = (TestSuiteResult)base.Result; } protected override void PerformWork() { if (!CheckForCancellation()) { if (base.Test.RunState != RunState.Explicit || base.Filter.IsExplicitMatch(base.Test)) { switch (base.Test.RunState) { default: base.Result.SetResult(ResultState.Success); if (Children.Count > 0) { InitializeSetUpAndTearDownCommands(); PerformOneTimeSetUp(); if (!CheckForCancellation()) { switch (base.Result.ResultState.Status) { case TestStatus.Passed: case TestStatus.Warning: RunChildren(); return; case TestStatus.Inconclusive: case TestStatus.Skipped: case TestStatus.Failed: SkipChildren(this, base.Result.ResultState.WithSite(FailureSite.Parent), "OneTimeSetUp: " + base.Result.Message, base.Result.StackTrace); break; } } if (base.Context.ExecutionStatus != TestExecutionStatus.AbortRequested) PerformOneTimeTearDown(); } else if (base.Test.TestType == "Theory") { base.Result.SetResult(ResultState.Failure, "No test cases were provided"); } break; case RunState.Skipped: SkipFixture(ResultState.Skipped, GetSkipReason(), null); break; case RunState.Ignored: SkipFixture(ResultState.Ignored, GetSkipReason(), null); break; case RunState.NotRunnable: SkipFixture(ResultState.NotRunnable, GetSkipReason(), GetProviderStackTrace()); break; } } else SkipFixture(ResultState.Explicit, GetSkipReason(), null); } WorkItemComplete(); } private bool CheckForCancellation() { if (base.Context.ExecutionStatus != 0) { base.Result.SetResult(ResultState.Cancelled, "Test cancelled by user"); return true; } return false; } private void InitializeSetUpAndTearDownCommands() { StaticMethodValidator methodValidator = base.Test.HasLifeCycle(LifeCycle.InstancePerTestCase) ? new StaticMethodValidator("Only static OneTimeSetUp and OneTimeTearDown are allowed for InstancePerTestCase mode.") : null; List<SetUpTearDownItem> list = BuildSetUpTearDownList(_suite.OneTimeSetUpMethods, _suite.OneTimeTearDownMethods, methodValidator); List<TestActionItem> list2 = new List<TestActionItem>(); ITestAction[] actions = base.Test.Actions; foreach (ITestAction testAction in actions) { bool flag = testAction.Targets.HasFlag(ActionTargets.Suite) || (testAction.Targets == ActionTargets.Default && !(base.Test is ParameterizedMethodSuite)); bool flag2 = testAction.Targets.HasFlag(ActionTargets.Test) && !(base.Test is ParameterizedMethodSuite); if (flag) list2.Add(new TestActionItem(testAction)); if (flag2) base.Context.UpstreamActions.Add(testAction); } _setupCommand = MakeOneTimeSetUpCommand(list, list2); _teardownCommand = MakeOneTimeTearDownCommand(list, list2); } private TestCommand MakeOneTimeSetUpCommand(List<SetUpTearDownItem> setUpTearDown, List<TestActionItem> actions) { TestCommand testCommand = new EmptyTestCommand(base.Test); int num = actions.Count; while (--num >= 0) { testCommand = new BeforeTestActionCommand(testCommand, actions[num]); } if (base.Test.TypeInfo != null) { foreach (SetUpTearDownItem item in setUpTearDown) { testCommand = new OneTimeSetUpCommand(testCommand, item); } if (!base.Test.TypeInfo.IsStaticClass && !base.Test.HasLifeCycle(LifeCycle.InstancePerTestCase)) testCommand = new ConstructFixtureCommand(testCommand); } IApplyToContext[] customAttributes = base.Test.GetCustomAttributes<IApplyToContext>(true); foreach (IApplyToContext change in customAttributes) { testCommand = new ApplyChangesToContextCommand(testCommand, change); } return testCommand; } private TestCommand MakeOneTimeTearDownCommand(List<SetUpTearDownItem> setUpTearDownItems, List<TestActionItem> actions) { TestCommand testCommand = new EmptyTestCommand(base.Test); if (base.Test.TestType == "Theory") testCommand = new TheoryResultCommand(testCommand); int num = actions.Count; while (--num >= 0) { testCommand = new AfterTestActionCommand(testCommand, actions[num]); } foreach (SetUpTearDownItem setUpTearDownItem in setUpTearDownItems) { testCommand = new OneTimeTearDownCommand(testCommand, setUpTearDownItem); } if (base.Test is IDisposableFixture && base.Test.TypeInfo != null && DisposeHelper.IsDisposable(base.Test.TypeInfo.Type) && !base.Test.HasLifeCycle(LifeCycle.InstancePerTestCase)) testCommand = new DisposeFixtureCommand(testCommand); return testCommand; } private void PerformOneTimeSetUp() { try { _setupCommand?.Execute(base.Context); base.Context.UpdateContextFromEnvironment(); } catch (Exception exception) { base.Result.RecordException(exception.Unwrap(), FailureSite.SetUp); } } private void RunChildren() { if (base.Test.TestType == "Theory") base.Result.SetResult(ResultState.Inconclusive); int num = Children.Count; if (num == 0) throw new InvalidOperationException("RunChildren called but item has no children"); _childTestCountdown = new CountdownEvent(num); foreach (WorkItem child in Children) { if (CheckForCancellation()) break; child.Completed += OnChildItemCompleted; child.InitializeContext(new TestExecutionContext(base.Context)); child.TestWorker = base.TestWorker; base.Context.Dispatcher.Dispatch(child); num--; } if (num > 0) { lock (_childCompletionLock) { _childTestCountdown.Signal(num); if (_childTestCountdown.CurrentCount == 0) OnAllChildItemsCompleted(); } } } private void SkipFixture(ResultState resultState, string message, [Nullable(2)] string stackTrace) { base.Result.SetResult(resultState.WithSite(FailureSite.SetUp), message, StackFilter.DefaultFilter.Filter(stackTrace)); SkipChildren(this, resultState.WithSite(FailureSite.Parent), "OneTimeSetUp: " + message, stackTrace); } private void SkipChildren(CompositeWorkItem workItem, ResultState resultState, string message, [Nullable(2)] string stackTrace) { foreach (WorkItem child in workItem.Children) { SetChildWorkItemSkippedResult(child.Result, resultState, message, stackTrace); _suiteResult.AddResult(child.Result); base.Context.Listener.TestFinished(child.Result); CompositeWorkItem compositeWorkItem = child as CompositeWorkItem; if (compositeWorkItem != null) SkipChildren(compositeWorkItem, resultState, message, stackTrace); } } private void SetChildWorkItemSkippedResult(TestResult result, ResultState resultState, string message, [Nullable(2)] string stackTrace) { result.SetResult(resultState, message, stackTrace); result.StartTime = base.Context.StartTime; result.EndTime = DateTime.UtcNow; result.Duration = base.Context.Duration; } private void PerformOneTimeTearDown() { base.Context.EstablishExecutionEnvironment(); _teardownCommand?.Execute(base.Context); } private string GetSkipReason() { return ((string)base.Test.Properties.Get("_SKIPREASON")) ?? string.Empty; } [NullableContext(2)] private string GetProviderStackTrace() { return (string)base.Test.Properties.Get("_PROVIDERSTACKTRACE"); } private void OnChildItemCompleted([Nullable(2)] object sender, EventArgs e) { lock (_childCompletionLock) { WorkItem workItem = sender as WorkItem; if (workItem != null) { workItem.Completed -= OnChildItemCompleted; _suiteResult.AddResult(workItem.Result); if (base.Context.StopOnError && workItem.Result.ResultState.Status == TestStatus.Failed) base.Context.ExecutionStatus = TestExecutionStatus.StopRequested; _childTestCountdown.Signal(); if (_childTestCountdown.CurrentCount == 0) OnAllChildItemsCompleted(); } } } private void OnAllChildItemsCompleted() { if (base.Context.ExecutionStatus == TestExecutionStatus.AbortRequested) WorkItemComplete(); else base.Context.Dispatcher.Dispatch(new OneTimeTearDownWorkItem(this)); } public override void Cancel(bool force) { lock (_cancelLock) { foreach (WorkItem child in Children) { TestExecutionContext context = child.Context; if (context != null) context.ExecutionStatus = ((!force) ? TestExecutionStatus.StopRequested : TestExecutionStatus.AbortRequested); if (child.State == WorkItemState.Running) child.Cancel(force); } } } } }