A game about forced loneliness, made by TACStudios
1using System;
2using System.Collections;
3using System.Collections.Generic;
4using System.Linq;
5using NUnit.Framework.Interfaces;
6using NUnit.Framework.Internal;
7using UnityEngine;
8using UnityEngine.TestRunner.NUnitExtensions;
9using UnityEngine.TestRunner.NUnitExtensions.Runner;
10using UnityEngine.TestTools;
11using UnityEngine.TestTools.NUnitExtensions;
12using UnityEngine.TestTools.TestRunner;
13
14namespace UnityEditor.TestTools.TestRunner
15{
16 internal interface IUnityTestAssemblyRunnerFactory
17 {
18 IUnityTestAssemblyRunner Create(TestPlatform testPlatform, string[] orderedTestNames, int randomOrderSeed, WorkItemFactory factory, UnityTestExecutionContext context);
19 }
20
21 internal class UnityTestAssemblyRunnerFactory : IUnityTestAssemblyRunnerFactory
22 {
23 public IUnityTestAssemblyRunner Create(TestPlatform testPlatform, string[] orderedTestNames, int randomOrderSeed,
24 WorkItemFactory factory, UnityTestExecutionContext context)
25 {
26 return new UnityTestAssemblyRunner(new UnityTestAssemblyBuilder(orderedTestNames, randomOrderSeed), factory, context);
27 }
28 }
29
30 [Serializable]
31 internal class EditModeRunner : ScriptableObject, IDisposable
32 {
33 //The counter from the IEnumerator object
34 [SerializeField]
35 private int m_CurrentPC;
36
37 [SerializeField]
38 private bool m_ExecuteOnEnable;
39
40 [SerializeField]
41 private List<string> m_AlreadyStartedTests;
42
43 [SerializeField]
44 private List<TestResultSerializer> m_ExecutedTests;
45
46 private TestStartedEvent m_TestStartedEvent;
47 private TestFinishedEvent m_TestFinishedEvent;
48
49 [SerializeField]
50 private object m_CurrentYieldObject;
51
52 [SerializeField]
53 private string[] m_OrderedTestNames;
54
55 [SerializeField]
56 public bool RunFinished;
57
58 [SerializeField]
59 private bool m_DisableNestedEnumeratorBugfix;
60
61 public bool RunningSynchronously { get; private set; }
62
63 internal IUnityTestAssemblyRunner m_Runner;
64
65 private IEnumerator m_RunStep;
66
67 public IUnityTestAssemblyRunnerFactory UnityTestAssemblyRunnerFactory { get; set; }
68
69 public void Init(ITestFilter filter, bool runningSynchronously, ITest testTree, TestStartedEvent testStartedEvent, TestFinishedEvent testFinishedEvent, UnityTestExecutionContext context,
70 string[] orderedTestNames, int randomOrderSeed, bool disableNestedEnumeratorBugfix)
71 {
72 TestEnumerator.Reset();
73 m_AlreadyStartedTests = new List<string>();
74 m_ExecutedTests = new List<TestResultSerializer>();
75 m_OrderedTestNames = orderedTestNames;
76 m_randomOrderSeed = randomOrderSeed;
77 RunningSynchronously = runningSynchronously;
78 m_DisableNestedEnumeratorBugfix = disableNestedEnumeratorBugfix;
79 Run(testTree, filter, context, testStartedEvent, testFinishedEvent);
80 }
81
82 public void Resume(ITestFilter filter, ITest testTree, TestStartedEvent testStartedEvent, TestFinishedEvent testFinishedEvent, UnityTestExecutionContext context)
83 {
84 if (m_ExecuteOnEnable)
85 {
86 m_ExecuteOnEnable = false;
87
88 if (m_CurrentPC >= 0)
89 {
90 EnumeratorStepHelper.SetEnumeratorPC(m_CurrentPC);
91 }
92
93 UnityWorkItemDataHolder.alreadyExecutedTests = m_ExecutedTests.Select(x => x.uniqueName).ToList();
94 UnityWorkItemDataHolder.alreadyStartedTests = m_AlreadyStartedTests;
95 Run(testTree, filter, context, testStartedEvent, testFinishedEvent);
96 }
97 }
98
99 public void TestStartedEvent(ITest test)
100 {
101 m_AlreadyStartedTests.Add(test.GetUniqueName());
102 }
103
104 public void TestFinishedEvent(ITestResult testResult)
105 {
106 m_AlreadyStartedTests.Remove(testResult.Test.GetUniqueName());
107 m_ExecutedTests.Add(TestResultSerializer.MakeFromTestResult(testResult));
108 }
109
110 private void Run(ITest testTree, ITestFilter filter, UnityTestExecutionContext context, TestStartedEvent testStartedEvent, TestFinishedEvent testFinishedEvent)
111 {
112 m_TestStartedEvent = testStartedEvent;
113 m_TestFinishedEvent = testFinishedEvent;
114
115 m_Runner = (UnityTestAssemblyRunnerFactory ?? new UnityTestAssemblyRunnerFactory()).Create(TestPlatform.EditMode, m_OrderedTestNames, m_randomOrderSeed, new EditmodeWorkItemFactory(), context);
116 m_Runner.LoadTestTree(testTree);
117 hideFlags |= HideFlags.DontSave;
118 EnumeratorHelper.ActivePcHelper = new EditModePcHelper();
119
120 EditModeTestCallbacks.RestoringTestContext += OnRestoringTest;
121
122 m_TestStartedEvent.AddListener(TestStartedEvent);
123 m_TestFinishedEvent.AddListener(TestFinishedEvent);
124
125 AssemblyReloadEvents.beforeAssemblyReload += OnBeforeAssemblyReload;
126
127 RunningTests = true;
128
129 EditorApplication.LockReloadAssemblies();
130
131 var testListenerWrapper = new TestListenerWrapper(m_TestStartedEvent, m_TestFinishedEvent);
132 m_RunStep = m_Runner.Run(testListenerWrapper, filter).GetEnumerator();
133 }
134
135 private void OnBeforeAssemblyReload()
136 {
137 if (m_ExecuteOnEnable)
138 {
139 AssemblyReloadEvents.beforeAssemblyReload -= OnBeforeAssemblyReload;
140 return;
141 }
142
143 if (m_Runner != null && m_Runner.TopLevelWorkItem != null)
144 m_Runner.TopLevelWorkItem.ResultedInDomainReload = true;
145
146 if (RunningTests)
147 {
148 Debug.LogError("TestRunner: Unexpected assembly reload happened while running tests");
149
150 EditorUtility.ClearProgressBar();
151
152 if (m_Runner.GetCurrentContext() != null && m_Runner.GetCurrentContext().CurrentResult != null)
153 {
154 m_Runner.GetCurrentContext().CurrentResult.SetResult(ResultState.Cancelled, "Unexpected assembly reload happened");
155 }
156 OnRunCancel();
157 }
158 }
159
160 private bool RunningTests;
161
162 private Stack<IEnumerator> StepStack = new Stack<IEnumerator>();
163 private int m_randomOrderSeed;
164
165 private bool MoveNextAndUpdateYieldObject()
166 {
167 var result = m_RunStep.MoveNext();
168
169 if (result)
170 {
171 m_CurrentYieldObject = m_RunStep.Current;
172 while (m_CurrentYieldObject is IEnumerator) // going deeper
173 {
174 var currentEnumerator = (IEnumerator)m_CurrentYieldObject;
175
176 // go deeper and add parent to stack
177 StepStack.Push(m_RunStep);
178
179 m_RunStep = currentEnumerator;
180 m_CurrentYieldObject = m_RunStep.Current;
181
182 if (!m_DisableNestedEnumeratorBugfix)
183 {
184 return MoveNextAndUpdateYieldObject();
185 }
186 }
187
188 if (StepStack.Count > 0 && m_CurrentYieldObject != null) // not null and not IEnumerator, nested
189 {
190 Debug.LogError("EditMode test can only yield null, but not <" + m_CurrentYieldObject.GetType().Name + ">");
191 }
192
193 return true;
194 }
195
196 if (StepStack.Count == 0) // done
197 return false;
198
199 m_RunStep = StepStack.Pop(); // going up
200 return MoveNextAndUpdateYieldObject();
201 }
202
203 public void TestConsumer(TestRunnerStateSerializer testRunnerStateSerializer)
204 {
205 var moveNext = MoveNextAndUpdateYieldObject();
206
207 if (m_CurrentYieldObject != null)
208 {
209 InvokeDelegator(testRunnerStateSerializer);
210 }
211
212 if (!moveNext && !m_Runner.IsTestComplete)
213 {
214 CompleteTestRun();
215 throw new IndexOutOfRangeException("There are no more elements to process and IsTestComplete is false");
216 }
217
218 if (m_Runner.IsTestComplete)
219 {
220 CompleteTestRun();
221 }
222 }
223
224 private void CompleteTestRun()
225 {
226 RunFinished = true;
227 UnityWorkItemDataHolder.alreadyExecutedTests = null;
228 }
229
230 private void OnRestoringTest()
231 {
232 var item = m_ExecutedTests.Find(t => t.fullName == UnityTestExecutionContext.CurrentContext.CurrentTest.FullName);
233 if (item != null)
234 {
235 item.RestoreTestResult(UnityTestExecutionContext.CurrentContext.CurrentResult);
236 }
237 }
238
239 private static bool IsCancelled()
240 {
241 return UnityTestExecutionContext.CurrentContext.ExecutionStatus == TestExecutionStatus.AbortRequested || UnityTestExecutionContext.CurrentContext.ExecutionStatus == TestExecutionStatus.StopRequested;
242 }
243
244 private void InvokeDelegator(TestRunnerStateSerializer testRunnerStateSerializer)
245 {
246 if (m_CurrentYieldObject == null)
247 {
248 return;
249 }
250
251 if (IsCancelled())
252 {
253 return;
254 }
255
256 if (m_CurrentYieldObject is RestoreTestContextAfterDomainReload)
257 {
258 if (testRunnerStateSerializer.ShouldRestore())
259 {
260 testRunnerStateSerializer.RestoreContext();
261 }
262
263 return;
264 }
265
266 try
267 {
268 if (m_CurrentYieldObject is IEditModeTestYieldInstruction)
269 {
270 var editModeTestYieldInstruction = (IEditModeTestYieldInstruction)m_CurrentYieldObject;
271 if (editModeTestYieldInstruction.ExpectDomainReload)
272 {
273 PrepareForDomainReload(testRunnerStateSerializer);
274 }
275 return;
276 }
277 }
278 catch (Exception e)
279 {
280 UnityTestExecutionContext.CurrentContext.CurrentResult.RecordException(e);
281 return;
282 }
283
284 UnityTestExecutionContext.CurrentContext.CurrentResult.RecordException(new InvalidOperationException("EditMode test can only yield null"));
285 }
286
287 private void CompilationFailureWatch()
288 {
289 if (EditorApplication.isCompiling)
290 return;
291
292 EditorApplication.update -= CompilationFailureWatch;
293
294 if (EditorUtility.scriptCompilationFailed)
295 {
296 EditorUtility.ClearProgressBar();
297 OnRunCancel();
298 }
299 }
300
301 private void PrepareForDomainReload(TestRunnerStateSerializer testRunnerStateSerializer)
302 {
303 testRunnerStateSerializer.SaveContext();
304 m_CurrentPC = EnumeratorStepHelper.GetEnumeratorPC(TestEnumerator.Enumerator);
305 m_ExecuteOnEnable = true;
306
307 RunningTests = false;
308 }
309
310 public void Dispose()
311 {
312 Reflect.MethodCallWrapper = null;
313
314 DestroyImmediate(this);
315
316 RunningTests = false;
317 EditorApplication.UnlockReloadAssemblies();
318 }
319
320 public void OnRunCancel()
321 {
322 UnityWorkItemDataHolder.alreadyExecutedTests = null;
323 m_ExecuteOnEnable = false;
324 m_Runner.StopRun();
325 RunFinished = true;
326 }
327 }
328}