mathe/Library/PackageCache/com.unity.test-framework@1.1.33/UnityEngine.TestRunner/NUnitExtensions/Runner/CompositeWorkItem.cs
2024-09-20 20:30:10 +02:00

355 lines
13 KiB
C#

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using NUnit.Framework;
using NUnit.Framework.Interfaces;
using NUnit.Framework.Internal;
using NUnit.Framework.Internal.Commands;
using NUnit.Framework.Internal.Execution;
using UnityEngine.TestTools.Logging;
using UnityEngine.TestTools.TestRunner;
using CountdownEvent = System.Threading.CountdownEvent;
namespace UnityEngine.TestRunner.NUnitExtensions.Runner
{
internal class CompositeWorkItem : UnityWorkItem
{
private readonly TestSuite _suite;
private readonly TestSuiteResult _suiteResult;
private readonly ITestFilter _childFilter;
private TestCommand _setupCommand;
private TestCommand _teardownCommand;
public List<UnityWorkItem> Children { get; private set; }
private int _countOrder;
private CountdownEvent _childTestCountdown;
public CompositeWorkItem(TestSuite suite, ITestFilter childFilter, WorkItemFactory factory)
: base(suite, factory)
{
_suite = suite;
_suiteResult = Result as TestSuiteResult;
_childFilter = childFilter;
_countOrder = 0;
}
protected override IEnumerable PerformWork()
{
InitializeSetUpAndTearDownCommands();
if (UnityTestExecutionContext.CurrentContext != null && m_DontRunRestoringResult && EditModeTestCallbacks.RestoringTestContext != null)
{
EditModeTestCallbacks.RestoringTestContext();
}
if (!CheckForCancellation())
if (Test.RunState == RunState.Explicit && !_childFilter.IsExplicitMatch(Test))
SkipFixture(ResultState.Explicit, GetSkipReason(), null);
else
switch (Test.RunState)
{
default:
case RunState.Runnable:
case RunState.Explicit:
Result.SetResult(ResultState.Success);
CreateChildWorkItems();
if (Children.Count > 0)
{
if (!m_DontRunRestoringResult)
{
//This is needed to give the editor a chance to go out of playmode if needed before creating objects.
//If we do not, the objects could be automatically destroyed when exiting playmode and could result in errors later on
yield return null;
PerformOneTimeSetUp();
}
if (!CheckForCancellation())
{
switch (Result.ResultState.Status)
{
case TestStatus.Passed:
foreach (var child in RunChildren())
{
if (CheckForCancellation())
{
yield break;
}
yield return child;
}
break;
case TestStatus.Skipped:
case TestStatus.Inconclusive:
case TestStatus.Failed:
SkipChildren(_suite, Result.ResultState.WithSite(FailureSite.Parent), "OneTimeSetUp: " + Result.Message);
break;
}
}
if (Context.ExecutionStatus != TestExecutionStatus.AbortRequested && !m_DontRunRestoringResult)
{
PerformOneTimeTearDown();
}
}
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;
}
if (!ResultedInDomainReload)
{
WorkItemComplete();
}
}
private bool CheckForCancellation()
{
if (Context.ExecutionStatus != TestExecutionStatus.Running)
{
Result.SetResult(ResultState.Cancelled, "Test cancelled by user");
return true;
}
return false;
}
private void InitializeSetUpAndTearDownCommands()
{
List<SetUpTearDownItem> setUpTearDownItems = _suite.TypeInfo != null
? CommandBuilder.BuildSetUpTearDownList(_suite.TypeInfo.Type, typeof(OneTimeSetUpAttribute), typeof(OneTimeTearDownAttribute))
: new List<SetUpTearDownItem>();
var actionItems = new List<TestActionItem>();
foreach (ITestAction action in Actions)
{
bool applyToSuite = (action.Targets & ActionTargets.Suite) == ActionTargets.Suite
|| action.Targets == ActionTargets.Default && !(Test is ParameterizedMethodSuite);
bool applyToTest = (action.Targets & ActionTargets.Test) == ActionTargets.Test
&& !(Test is ParameterizedMethodSuite);
if (applyToSuite)
actionItems.Add(new TestActionItem(action));
if (applyToTest)
Context.UpstreamActions.Add(action);
}
_setupCommand = CommandBuilder.MakeOneTimeSetUpCommand(_suite, setUpTearDownItems, actionItems);
_teardownCommand = CommandBuilder.MakeOneTimeTearDownCommand(_suite, setUpTearDownItems, actionItems);
}
private void PerformOneTimeSetUp()
{
var logScope = new LogScope();
try
{
_setupCommand.Execute(Context);
logScope.EvaluateLogScope(true);
}
catch (Exception ex)
{
if (ex is NUnitException || ex is TargetInvocationException)
ex = ex.InnerException;
Result.RecordException(ex, FailureSite.SetUp);
}
logScope.Dispose();
}
private IEnumerable RunChildren()
{
int childCount = Children.Count;
if (childCount == 0)
throw new InvalidOperationException("RunChildren called but item has no children");
_childTestCountdown = new CountdownEvent(childCount);
foreach (UnityWorkItem child in Children)
{
if (CheckForCancellation())
{
yield break;
}
var unityTestExecutionContext = new UnityTestExecutionContext(Context);
child.InitializeContext(unityTestExecutionContext);
var enumerable = child.Execute().GetEnumerator();
while (true)
{
if (!enumerable.MoveNext())
{
break;
}
ResultedInDomainReload |= child.ResultedInDomainReload;
yield return enumerable.Current;
}
_suiteResult.AddResult(child.Result);
childCount--;
}
if (childCount > 0)
{
while (childCount-- > 0)
CountDownChildTest();
}
}
private void CreateChildWorkItems()
{
Children = new List<UnityWorkItem>();
var testSuite = _suite;
foreach (ITest test in testSuite.Tests)
{
if (_childFilter.Pass(test))
{
var child = m_Factory.Create(test, _childFilter);
if (test.Properties.ContainsKey(PropertyNames.Order))
{
Children.Insert(0, child);
_countOrder++;
}
else
{
Children.Add(child);
}
}
}
if (_countOrder != 0) SortChildren();
}
private class UnityWorkItemOrderComparer : IComparer<UnityWorkItem>
{
public int Compare(UnityWorkItem x, UnityWorkItem y)
{
var xKey = int.MaxValue;
var yKey = int.MaxValue;
if (x.Test.Properties.ContainsKey(PropertyNames.Order))
xKey = (int)x.Test.Properties[PropertyNames.Order][0];
if (y.Test.Properties.ContainsKey(PropertyNames.Order))
yKey = (int)y.Test.Properties[PropertyNames.Order][0];
return xKey.CompareTo(yKey);
}
}
private void SortChildren()
{
Children.Sort(0, _countOrder, new UnityWorkItemOrderComparer());
}
private void SkipFixture(ResultState resultState, string message, string stackTrace)
{
Result.SetResult(resultState.WithSite(FailureSite.SetUp), message, StackFilter.Filter(stackTrace));
SkipChildren(_suite, resultState.WithSite(FailureSite.Parent), "OneTimeSetUp: " + message);
}
private void SkipChildren(TestSuite suite, ResultState resultState, string message)
{
foreach (Test child in suite.Tests)
{
if (_childFilter.Pass(child))
{
Context.Listener.TestStarted(child);
TestResult childResult = child.MakeTestResult();
childResult.SetResult(resultState, message);
_suiteResult.AddResult(childResult);
if (child.IsSuite)
SkipChildren((TestSuite)child, resultState, message);
Context.Listener.TestFinished(childResult);
}
}
}
private void PerformOneTimeTearDown()
{
var logScope = new LogScope();
try
{
_teardownCommand.Execute(Context);
logScope.EvaluateLogScope(true);
}
catch (Exception ex)
{
if (ex is NUnitException || ex is TargetInvocationException)
ex = ex.InnerException;
Result.RecordException(ex, FailureSite.SetUp);
}
logScope.Dispose();
}
private string GetSkipReason()
{
return (string)Test.Properties.Get(PropertyNames.SkipReason);
}
private string GetProviderStackTrace()
{
return (string)Test.Properties.Get(PropertyNames.ProviderStackTrace);
}
private void CountDownChildTest()
{
_childTestCountdown.Signal();
if (_childTestCountdown.CurrentCount == 0)
{
if (Context.ExecutionStatus != TestExecutionStatus.AbortRequested)
PerformOneTimeTearDown();
foreach (var childResult in _suiteResult.Children)
if (childResult.ResultState == ResultState.Cancelled)
{
this.Result.SetResult(ResultState.Cancelled, "Cancelled by user");
break;
}
WorkItemComplete();
}
}
public override void Cancel(bool force)
{
if (Children == null)
return;
foreach (var child in Children)
{
var ctx = child.Context;
if (ctx != null)
ctx.ExecutionStatus = force ? TestExecutionStatus.AbortRequested : TestExecutionStatus.StopRequested;
if (child.State == WorkItemState.Running)
child.Cancel(force);
}
}
}
}