<PackageReference Include="NUnit" Version="3.10.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.Compatibility; using NUnit.Framework.Interfaces; using NUnit.Framework.Internal.Commands; using System; using System.Collections.Generic; using System.Reflection; namespace NUnit.Framework.Internal.Execution { public class CompositeWorkItem : WorkItem { public class OneTimeTearDownWorkItem : WorkItem { private CompositeWorkItem _originalWorkItem; private object _teardownLock = new object(); public override string Name => $"{base.Name}"""; 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() { } } private TestSuite _suite; private TestSuiteResult _suiteResult; private TestCommand _setupCommand; private TestCommand _teardownCommand; private CountdownEvent _childTestCountdown; private object _childCompletionLock = new object(); private 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 = (base.Result as TestSuiteResult); } 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); 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() { List<SetUpTearDownItem> list = BuildSetUpTearDownList(_suite.OneTimeSetUpMethods, _suite.OneTimeTearDownMethods); List<TestActionItem> list2 = new List<TestActionItem>(); ITestAction[] actions = base.Test.Actions; foreach (ITestAction testAction in actions) { bool flag = ActionTargetsExtensions.HasFlag(testAction.Targets, ActionTargets.Suite) || (testAction.Targets == ActionTargets.Default && !(base.Test is ParameterizedMethodSuite)); bool num = ActionTargetsExtensions.HasFlag(testAction.Targets, ActionTargets.Test) && !(base.Test is ParameterizedMethodSuite); if (flag) list2.Add(new TestActionItem(testAction)); if (num) 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) 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 && typeof(IDisposable).IsAssignableFrom(base.Test.TypeInfo.Type)) testCommand = new DisposeFixtureCommand(testCommand); return testCommand; } private void PerformOneTimeSetUp() { try { _setupCommand.Execute(base.Context); base.Context.UpdateContextFromEnvironment(); } catch (Exception ex) { Exception ex2 = ex; if (ex2 is NUnitException || ex2 is TargetInvocationException) ex2 = ex2.InnerException; base.Result.RecordException(ex2, 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, string stackTrace) { base.Result.SetResult(resultState.WithSite(FailureSite.SetUp), message, StackFilter.DefaultFilter.Filter(stackTrace)); SkipChildren(this, resultState.WithSite(FailureSite.Parent), "OneTimeSetUp: " + message); } private void SkipChildren(CompositeWorkItem workItem, ResultState resultState, string message) { foreach (WorkItem child in workItem.Children) { child.Result.SetResult(resultState, message); _suiteResult.AddResult(child.Result); base.Context.Listener.TestFinished(child.Result); if (child is CompositeWorkItem) SkipChildren((CompositeWorkItem)child, resultState, message); } } private void PerformOneTimeTearDown() { base.Context.EstablishExecutionEnvironment(); _teardownCommand.Execute(base.Context); } private string GetSkipReason() { return (string)base.Test.Properties.Get("_SKIPREASON"); } private string GetProviderStackTrace() { return (string)base.Test.Properties.Get("_PROVIDERSTACKTRACE"); } private void OnChildItemCompleted(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() { OneTimeTearDownWorkItem work = new OneTimeTearDownWorkItem(this); base.Context.Dispatcher.Dispatch(work); } private static bool IsStaticClass(Type type) { if (NUnit.Compatibility.TypeExtensions.GetTypeInfo(type).IsAbstract) return NUnit.Compatibility.TypeExtensions.GetTypeInfo(type).IsSealed; return false; } 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); } } } } }