323 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			323 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
| 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();
 | |
|                     }
 | |
|                 }
 | |
| 
 | |
|                 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;
 | |
|             }
 | |
| 
 | |
|         }
 | |
| 
 | |
|         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;
 | |
|         }
 | |
|     }
 | |
| }
 |