A game about forced loneliness, made by TACStudios
1using System; 2using System.Collections; 3using System.Collections.Generic; 4using System.Linq; 5using System.Reflection; 6using System.Threading; 7using NUnit.Framework; 8using NUnit.Framework.Interfaces; 9using NUnit.Framework.Internal; 10using NUnit.Framework.Internal.Commands; 11using NUnit.Framework.Internal.Execution; 12using UnityEngine.TestTools.Logging; 13 14namespace UnityEngine.TestRunner.NUnitExtensions.Runner 15{ 16 internal class CompositeWorkItem : UnityWorkItem 17 { 18 private readonly TestSuite _suite; 19 private readonly TestSuiteResult _suiteResult; 20 private readonly ITestFilter _childFilter; 21 private TestCommand _setupCommand; 22 private TestCommand _teardownCommand; 23 24 public List<UnityWorkItem> Children { get; private set; } 25 26 private int _countOrder; 27 28 private CountdownEvent _childTestCountdown; 29 30 public CompositeWorkItem(TestSuite suite, ITestFilter childFilter, WorkItemFactory factory) 31 : base(suite, factory) 32 { 33 _suite = suite; 34 _suiteResult = Result as TestSuiteResult; 35 _childFilter = childFilter; 36 _countOrder = 0; 37 } 38 39 protected override IEnumerable PerformWork() 40 { 41 InitializeSetUpAndTearDownCommands(); 42 43 if (UnityTestExecutionContext.CurrentContext != null && m_DontRunRestoringResult && EditModeTestCallbacks.RestoringTestContext != null) 44 { 45 EditModeTestCallbacks.RestoringTestContext(); 46 } 47 48 if (!CheckForCancellation()) 49 if (Test.RunState == RunState.Explicit && !_childFilter.IsExplicitMatch(Test)) 50 SkipFixture(ResultState.Explicit, GetSkipReason(), null); 51 else 52 switch (Test.RunState) 53 { 54 default: 55 case RunState.Runnable: 56 case RunState.Explicit: 57 Result.SetResult(ResultState.Success); 58 59 CreateChildWorkItems(); 60 61 if (Children.Count > 0) 62 { 63 if (!m_DontRunRestoringResult) 64 { 65 //This is needed to give the editor a chance to go out of playmode if needed before creating objects. 66 //If we do not, the objects could be automatically destroyed when exiting playmode and could result in errors later on 67 yield return null; 68 PerformOneTimeSetUp(); 69 } 70 71 if (!CheckForCancellation()) 72 { 73 switch (Result.ResultState.Status) 74 { 75 case TestStatus.Passed: 76 foreach (var child in RunChildren()) 77 { 78 if (CheckForCancellation()) 79 { 80 yield break; 81 } 82 83 yield return child; 84 } 85 break; 86 case TestStatus.Skipped: 87 case TestStatus.Inconclusive: 88 case TestStatus.Failed: 89 SkipChildren(_suite, Result.ResultState.WithSite(FailureSite.Parent), "OneTimeSetUp: " + Result.Message); 90 break; 91 } 92 } 93 94 if (Context.ExecutionStatus != TestExecutionStatus.AbortRequested && !m_DontRunRestoringResult) 95 { 96 PerformOneTimeTearDown(); 97 } 98 } 99 break; 100 101 case RunState.Skipped: 102 SkipFixture(ResultState.Skipped, GetSkipReason(), null); 103 break; 104 105 case RunState.Ignored: 106 SkipFixture(ResultState.Ignored, GetSkipReason(), null); 107 break; 108 109 case RunState.NotRunnable: 110 SkipFixture(ResultState.NotRunnable, GetSkipReason(), GetProviderStackTrace()); 111 break; 112 } 113 if (!ResultedInDomainReload) 114 { 115 WorkItemComplete(); 116 } 117 } 118 119 private bool CheckForCancellation() 120 { 121 if (Context.ExecutionStatus != TestExecutionStatus.Running) 122 { 123 Result.SetResult(ResultState.Cancelled, "Test cancelled by user"); 124 return true; 125 } 126 127 return false; 128 } 129 130 private void InitializeSetUpAndTearDownCommands() 131 { 132 List<SetUpTearDownItem> setUpTearDownItems = _suite.TypeInfo != null 133 ? CommandBuilder.BuildSetUpTearDownList(_suite.TypeInfo.Type, typeof(OneTimeSetUpAttribute), typeof(OneTimeTearDownAttribute)) 134 : new List<SetUpTearDownItem>(); 135 136 var actionItems = new List<TestActionItem>(); 137 foreach (ITestAction action in Actions) 138 { 139 bool applyToSuite = (action.Targets & ActionTargets.Suite) == ActionTargets.Suite 140 || action.Targets == ActionTargets.Default && !(Test is ParameterizedMethodSuite); 141 142 bool applyToTest = (action.Targets & ActionTargets.Test) == ActionTargets.Test 143 && !(Test is ParameterizedMethodSuite); 144 145 if (applyToSuite) 146 actionItems.Add(new TestActionItem(action)); 147 148 if (applyToTest) 149 Context.UpstreamActions.Add(action); 150 } 151 152 _setupCommand = CommandBuilder.MakeOneTimeSetUpCommand(_suite, setUpTearDownItems, actionItems); 153 _teardownCommand = CommandBuilder.MakeOneTimeTearDownCommand(_suite, setUpTearDownItems, actionItems); 154 } 155 156 private void PerformOneTimeSetUp() 157 { 158 var logScope = new LogScope(); 159 try 160 { 161 _setupCommand.Execute(Context); 162 logScope.EvaluateLogScope(true); 163 } 164 catch (Exception ex) 165 { 166 if (ex is NUnitException || ex is TargetInvocationException) 167 ex = ex.InnerException; 168 169 Result.RecordException(ex, FailureSite.SetUp); 170 } 171 172 logScope.Dispose(); 173 } 174 175 private IEnumerable RunChildren() 176 { 177 int childCount = Children.Count; 178 if (childCount == 0) 179 throw new InvalidOperationException("RunChildren called but item has no children"); 180 181 _childTestCountdown = new CountdownEvent(childCount); 182 183 foreach (UnityWorkItem child in Children) 184 { 185 if (CheckForCancellation()) 186 { 187 yield break; 188 } 189 190 var unityTestExecutionContext = new UnityTestExecutionContext(Context); 191 child.InitializeContext(unityTestExecutionContext); 192 193 var enumerable = child.Execute().GetEnumerator(); 194 195 while (true) 196 { 197 if (!enumerable.MoveNext()) 198 { 199 break; 200 } 201 ResultedInDomainReload |= child.ResultedInDomainReload; 202 yield return enumerable.Current; 203 } 204 205 _suiteResult.AddResult(child.Result); 206 childCount--; 207 } 208 209 if (childCount > 0) 210 { 211 while (childCount-- > 0) 212 CountDownChildTest(); 213 } 214 } 215 216 private void CreateChildWorkItems() 217 { 218 Children = new List<UnityWorkItem>(); 219 var testSuite = _suite; 220 221 foreach (ITest test in testSuite.Tests) 222 { 223 if (_childFilter.Pass(test)) 224 { 225 var child = m_Factory.Create(test, _childFilter); 226 227 if (test.Properties.ContainsKey(PropertyNames.Order)) 228 { 229 Children.Insert(0, child); 230 _countOrder++; 231 } 232 else 233 { 234 Children.Add(child); 235 } 236 } 237 } 238 239 if (_countOrder != 0) SortChildren(); 240 } 241 242 private class UnityWorkItemOrderComparer : IComparer<UnityWorkItem> 243 { 244 public int Compare(UnityWorkItem x, UnityWorkItem y) 245 { 246 var xKey = int.MaxValue; 247 var yKey = int.MaxValue; 248 249 if (x.Test.Properties.ContainsKey(PropertyNames.Order)) 250 xKey = (int)x.Test.Properties[PropertyNames.Order][0]; 251 252 if (y.Test.Properties.ContainsKey(PropertyNames.Order)) 253 yKey = (int)y.Test.Properties[PropertyNames.Order][0]; 254 255 return xKey.CompareTo(yKey); 256 } 257 } 258 259 private void SortChildren() 260 { 261 Children.Sort(0, _countOrder, new UnityWorkItemOrderComparer()); 262 } 263 264 private void SkipFixture(ResultState resultState, string message, string stackTrace) 265 { 266 Result.SetResult(resultState.WithSite(FailureSite.SetUp), message, StackFilter.Filter(stackTrace)); 267 SkipChildren(_suite, resultState.WithSite(FailureSite.Parent), message); 268 } 269 270 private void SkipChildren(TestSuite suite, ResultState resultState, string message) 271 { 272 foreach (Test child in suite.Tests) 273 { 274 if (_childFilter.Pass(child)) 275 { 276 if (!ShouldExecuteEvents(child)) 277 { 278 continue; 279 } 280 281 Context.Listener.TestStarted(child); 282 TestResult childResult = child.MakeTestResult(); 283 childResult.SetResult(resultState, message); 284 _suiteResult.AddResult(childResult); 285 286 if (child.IsSuite) 287 SkipChildren((TestSuite)child, resultState, message); 288 289 Context.Listener.TestFinished(childResult); 290 } 291 } 292 } 293 294 private static bool ShouldExecuteEvents(Test test) 295 { 296 return UnityWorkItemDataHolder.alreadyExecutedTests == null || UnityWorkItemDataHolder.alreadyExecutedTests.All(x => x != test.GetUniqueName()); 297 } 298 299 private void PerformOneTimeTearDown() 300 { 301 var logScope = new LogScope(); 302 try 303 { 304 _teardownCommand.Execute(Context); 305 logScope.EvaluateLogScope(true); 306 } 307 catch (Exception ex) 308 { 309 if (ex is NUnitException || ex is TargetInvocationException) 310 ex = ex.InnerException; 311 312 Result.RecordException(ex, FailureSite.SetUp); 313 } 314 315 logScope.Dispose(); 316 } 317 318 private string GetSkipReason() 319 { 320 return (string)Test.Properties.Get(PropertyNames.SkipReason); 321 } 322 323 private string GetProviderStackTrace() 324 { 325 return (string)Test.Properties.Get(PropertyNames.ProviderStackTrace); 326 } 327 328 private void CountDownChildTest() 329 { 330 _childTestCountdown.Signal(); 331 if (_childTestCountdown.CurrentCount == 0) 332 { 333 if (Context.ExecutionStatus != TestExecutionStatus.AbortRequested) 334 PerformOneTimeTearDown(); 335 336 foreach (var childResult in _suiteResult.Children) 337 if (childResult.ResultState == ResultState.Cancelled) 338 { 339 Result.SetResult(ResultState.Cancelled, "Cancelled by user"); 340 break; 341 } 342 343 WorkItemComplete(); 344 } 345 } 346 347 public override void Cancel(bool force) 348 { 349 if (Children == null) 350 return; 351 352 foreach (var child in Children) 353 { 354 var ctx = child.Context; 355 if (ctx != null) 356 ctx.ExecutionStatus = force ? TestExecutionStatus.AbortRequested : TestExecutionStatus.StopRequested; 357 358 if (child.State == WorkItemState.Running) 359 child.Cancel(force); 360 } 361 } 362 } 363}