CompositeWorkItem
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);
}
}
}
}
}