UnityGame/Library/PackageCache/com.unity.test-framework/UnityEditor.TestRunner/TestRunner/EditModeRunner.cs

329 lines
11 KiB
C#
Raw Permalink Normal View History

2024-10-27 10:53:47 +03:00
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework.Interfaces;
using NUnit.Framework.Internal;
using UnityEngine;
using UnityEngine.TestRunner.NUnitExtensions;
using UnityEngine.TestRunner.NUnitExtensions.Runner;
using UnityEngine.TestTools;
using UnityEngine.TestTools.NUnitExtensions;
using UnityEngine.TestTools.TestRunner;
namespace UnityEditor.TestTools.TestRunner
{
internal interface IUnityTestAssemblyRunnerFactory
{
IUnityTestAssemblyRunner Create(TestPlatform testPlatform, string[] orderedTestNames, int randomOrderSeed, WorkItemFactory factory, UnityTestExecutionContext context);
}
internal class UnityTestAssemblyRunnerFactory : IUnityTestAssemblyRunnerFactory
{
public IUnityTestAssemblyRunner Create(TestPlatform testPlatform, string[] orderedTestNames, int randomOrderSeed,
WorkItemFactory factory, UnityTestExecutionContext context)
{
return new UnityTestAssemblyRunner(new UnityTestAssemblyBuilder(orderedTestNames, randomOrderSeed), factory, context);
}
}
[Serializable]
internal class EditModeRunner : ScriptableObject, IDisposable
{
//The counter from the IEnumerator object
[SerializeField]
private int m_CurrentPC;
[SerializeField]
private bool m_ExecuteOnEnable;
[SerializeField]
private List<string> m_AlreadyStartedTests;
[SerializeField]
private List<TestResultSerializer> m_ExecutedTests;
private TestStartedEvent m_TestStartedEvent;
private TestFinishedEvent m_TestFinishedEvent;
[SerializeField]
private object m_CurrentYieldObject;
[SerializeField]
private string[] m_OrderedTestNames;
[SerializeField]
public bool RunFinished;
[SerializeField]
private bool m_DisableNestedEnumeratorBugfix;
public bool RunningSynchronously { get; private set; }
internal IUnityTestAssemblyRunner m_Runner;
private IEnumerator m_RunStep;
public IUnityTestAssemblyRunnerFactory UnityTestAssemblyRunnerFactory { get; set; }
public void Init(ITestFilter filter, bool runningSynchronously, ITest testTree, TestStartedEvent testStartedEvent, TestFinishedEvent testFinishedEvent, UnityTestExecutionContext context,
string[] orderedTestNames, int randomOrderSeed, bool disableNestedEnumeratorBugfix)
{
TestEnumerator.Reset();
m_AlreadyStartedTests = new List<string>();
m_ExecutedTests = new List<TestResultSerializer>();
m_OrderedTestNames = orderedTestNames;
m_randomOrderSeed = randomOrderSeed;
RunningSynchronously = runningSynchronously;
m_DisableNestedEnumeratorBugfix = disableNestedEnumeratorBugfix;
Run(testTree, filter, context, testStartedEvent, testFinishedEvent);
}
public void Resume(ITestFilter filter, ITest testTree, TestStartedEvent testStartedEvent, TestFinishedEvent testFinishedEvent, UnityTestExecutionContext context)
{
if (m_ExecuteOnEnable)
{
m_ExecuteOnEnable = false;
if (m_CurrentPC >= 0)
{
EnumeratorStepHelper.SetEnumeratorPC(m_CurrentPC);
}
UnityWorkItemDataHolder.alreadyExecutedTests = m_ExecutedTests.Select(x => x.uniqueName).ToList();
UnityWorkItemDataHolder.alreadyStartedTests = m_AlreadyStartedTests;
Run(testTree, filter, context, testStartedEvent, testFinishedEvent);
}
}
public void TestStartedEvent(ITest test)
{
m_AlreadyStartedTests.Add(test.GetUniqueName());
}
public void TestFinishedEvent(ITestResult testResult)
{
m_AlreadyStartedTests.Remove(testResult.Test.GetUniqueName());
m_ExecutedTests.Add(TestResultSerializer.MakeFromTestResult(testResult));
}
private void Run(ITest testTree, ITestFilter filter, UnityTestExecutionContext context, TestStartedEvent testStartedEvent, TestFinishedEvent testFinishedEvent)
{
m_TestStartedEvent = testStartedEvent;
m_TestFinishedEvent = testFinishedEvent;
m_Runner = (UnityTestAssemblyRunnerFactory ?? new UnityTestAssemblyRunnerFactory()).Create(TestPlatform.EditMode, m_OrderedTestNames, m_randomOrderSeed, new EditmodeWorkItemFactory(), context);
m_Runner.LoadTestTree(testTree);
hideFlags |= HideFlags.DontSave;
EnumeratorHelper.ActivePcHelper = new EditModePcHelper();
EditModeTestCallbacks.RestoringTestContext += OnRestoringTest;
m_TestStartedEvent.AddListener(TestStartedEvent);
m_TestFinishedEvent.AddListener(TestFinishedEvent);
AssemblyReloadEvents.beforeAssemblyReload += OnBeforeAssemblyReload;
RunningTests = true;
EditorApplication.LockReloadAssemblies();
var testListenerWrapper = new TestListenerWrapper(m_TestStartedEvent, m_TestFinishedEvent);
m_RunStep = m_Runner.Run(testListenerWrapper, filter).GetEnumerator();
}
private void OnBeforeAssemblyReload()
{
if (m_ExecuteOnEnable)
{
AssemblyReloadEvents.beforeAssemblyReload -= OnBeforeAssemblyReload;
return;
}
if (m_Runner != null && m_Runner.TopLevelWorkItem != null)
m_Runner.TopLevelWorkItem.ResultedInDomainReload = true;
if (RunningTests)
{
Debug.LogError("TestRunner: Unexpected assembly reload happened while running tests");
EditorUtility.ClearProgressBar();
if (m_Runner.GetCurrentContext() != null && m_Runner.GetCurrentContext().CurrentResult != null)
{
m_Runner.GetCurrentContext().CurrentResult.SetResult(ResultState.Cancelled, "Unexpected assembly reload happened");
}
OnRunCancel();
}
}
private bool RunningTests;
private Stack<IEnumerator> StepStack = new Stack<IEnumerator>();
private int m_randomOrderSeed;
private bool MoveNextAndUpdateYieldObject()
{
var result = m_RunStep.MoveNext();
if (result)
{
m_CurrentYieldObject = m_RunStep.Current;
while (m_CurrentYieldObject is IEnumerator) // going deeper
{
var currentEnumerator = (IEnumerator)m_CurrentYieldObject;
// go deeper and add parent to stack
StepStack.Push(m_RunStep);
m_RunStep = currentEnumerator;
m_CurrentYieldObject = m_RunStep.Current;
if (!m_DisableNestedEnumeratorBugfix)
{
return MoveNextAndUpdateYieldObject();
}
}
if (StepStack.Count > 0 && m_CurrentYieldObject != null) // not null and not IEnumerator, nested
{
Debug.LogError("EditMode test can only yield null, but not <" + m_CurrentYieldObject.GetType().Name + ">");
}
return true;
}
if (StepStack.Count == 0) // done
return false;
m_RunStep = StepStack.Pop(); // going up
return MoveNextAndUpdateYieldObject();
}
public void TestConsumer(TestRunnerStateSerializer testRunnerStateSerializer)
{
var moveNext = MoveNextAndUpdateYieldObject();
if (m_CurrentYieldObject != null)
{
InvokeDelegator(testRunnerStateSerializer);
}
if (!moveNext && !m_Runner.IsTestComplete)
{
CompleteTestRun();
throw new IndexOutOfRangeException("There are no more elements to process and IsTestComplete is false");
}
if (m_Runner.IsTestComplete)
{
CompleteTestRun();
}
}
private void CompleteTestRun()
{
RunFinished = true;
UnityWorkItemDataHolder.alreadyExecutedTests = null;
}
private void OnRestoringTest()
{
var item = m_ExecutedTests.Find(t => t.fullName == UnityTestExecutionContext.CurrentContext.CurrentTest.FullName);
if (item != null)
{
item.RestoreTestResult(UnityTestExecutionContext.CurrentContext.CurrentResult);
}
}
private static bool IsCancelled()
{
return UnityTestExecutionContext.CurrentContext.ExecutionStatus == TestExecutionStatus.AbortRequested || UnityTestExecutionContext.CurrentContext.ExecutionStatus == TestExecutionStatus.StopRequested;
}
private void InvokeDelegator(TestRunnerStateSerializer testRunnerStateSerializer)
{
if (m_CurrentYieldObject == null)
{
return;
}
if (IsCancelled())
{
return;
}
if (m_CurrentYieldObject is RestoreTestContextAfterDomainReload)
{
if (testRunnerStateSerializer.ShouldRestore())
{
testRunnerStateSerializer.RestoreContext();
}
return;
}
try
{
if (m_CurrentYieldObject is IEditModeTestYieldInstruction)
{
var editModeTestYieldInstruction = (IEditModeTestYieldInstruction)m_CurrentYieldObject;
if (editModeTestYieldInstruction.ExpectDomainReload)
{
PrepareForDomainReload(testRunnerStateSerializer);
}
return;
}
}
catch (Exception e)
{
UnityTestExecutionContext.CurrentContext.CurrentResult.RecordException(e);
return;
}
UnityTestExecutionContext.CurrentContext.CurrentResult.RecordException(new InvalidOperationException("EditMode test can only yield null"));
}
private void CompilationFailureWatch()
{
if (EditorApplication.isCompiling)
return;
EditorApplication.update -= CompilationFailureWatch;
if (EditorUtility.scriptCompilationFailed)
{
EditorUtility.ClearProgressBar();
OnRunCancel();
}
}
private void PrepareForDomainReload(TestRunnerStateSerializer testRunnerStateSerializer)
{
testRunnerStateSerializer.SaveContext();
m_CurrentPC = EnumeratorStepHelper.GetEnumeratorPC(TestEnumerator.Enumerator);
m_ExecuteOnEnable = true;
RunningTests = false;
}
public void Dispose()
{
Reflect.MethodCallWrapper = null;
DestroyImmediate(this);
RunningTests = false;
EditorApplication.UnlockReloadAssemblies();
}
public void OnRunCancel()
{
UnityWorkItemDataHolder.alreadyExecutedTests = null;
m_ExecuteOnEnable = false;
m_Runner.StopRun();
RunFinished = true;
}
}
}