1 // ***********************************************************************
2 // Copyright (c) 2012 Charlie Poole
4 // Permission is hereby granted, free of charge, to any person obtaining
5 // a copy of this software and associated documentation files (the
6 // "Software"), to deal in the Software without restriction, including
7 // without limitation the rights to use, copy, modify, merge, publish,
8 // distribute, sublicense, and/or sell copies of the Software, and to
9 // permit persons to whom the Software is furnished to do so, subject to
10 // the following conditions:
12 // The above copyright notice and this permission notice shall be
13 // included in all copies or substantial portions of the Software.
15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 // ***********************************************************************
25 #define NUNIT_FRAMEWORK
30 using System.Collections.Generic;
31 using System.Threading;
32 using System.Reflection;
33 using NUnit.Compatibility;
34 using NUnit.Framework.Internal.Commands;
35 using NUnit.Framework.Interfaces;
36 using NUnit.Framework.TUnit;
37 using System.Collections.Concurrent;
38 using System.Threading.Tasks;
40 namespace NUnit.Framework.Internal.Execution
43 /// A CompositeWorkItem represents a test suite and
44 /// encapsulates the execution of the suite as well
45 /// as all its child tests.
47 public class CompositeWorkItem : WorkItem
49 // static Logger log = InternalTrace.GetLogger("CompositeWorkItem");
51 private TestSuite _suite;
52 private TestSuiteResult _suiteResult;
53 private ITestFilter _childFilter;
54 private TestCommand _setupCommand;
55 private TestCommand _teardownCommand;
56 private List<WorkItem> _children;
57 private TSettings tsettings;
60 /// List of Child WorkItems
62 public List<WorkItem> Children
64 get { return _children; }
65 private set { _children = value; }
69 /// A count of how many tests in the work item have a value for the Order Property
71 private int _countOrder;
73 private CountdownEvent _childTestCountdown;
76 /// Construct a CompositeWorkItem for executing a test suite
77 /// using a filter to select child tests.
79 /// <param name="suite">The TestSuite to be executed</param>
80 /// <param name="childFilter">A filter used to select child tests</param>
81 public CompositeWorkItem(TestSuite suite, ITestFilter childFilter)
85 _suiteResult = Result as TestSuiteResult;
86 _childFilter = childFilter;
88 tsettings = TSettings.GetInstance();
92 /// Method that actually performs the work. Overridden
93 /// in CompositeWorkItem to do setup, run all child
94 /// items and then do teardown.
96 protected override void PerformWork()
98 // Inititialize actions, setup and teardown
99 // We can't do this in the constructor because
100 // the context is not available at that point.
101 InitializeSetUpAndTearDownCommands();
103 if (!CheckForCancellation())
104 // [tronghieu.d] - Add more conditions for check slave mode
105 if (Test.TestType.Equals("Assembly") || !tsettings.IsSlaveMode || tsettings.Testcase_ID.Contains(Test.FullName))
106 // [tronghieu.d] - end
107 if (Test.RunState == RunState.Explicit && !_childFilter.IsExplicitMatch(Test))
108 SkipFixture(ResultState.Explicit, GetSkipReason(), null);
110 switch (Test.RunState)
113 case RunState.Runnable:
114 case RunState.Explicit:
115 // Assume success, since the result will be inconclusive
116 // if there is no setup method to run or if the
117 // context initialization fails.
118 Result.SetResult(ResultState.Success);
120 if (_children == null)
121 CreateChildWorkItems();
123 if (_children.Count > 0)
125 PerformOneTimeSetUp();
127 if (!CheckForCancellation())
128 switch (Result.ResultState.Status)
130 case TestStatus.Passed:
133 // Just return: completion event will take care
134 // of TestFixtureTearDown when all tests are done.
136 case TestStatus.Skipped:
137 case TestStatus.Inconclusive:
138 case TestStatus.Failed:
139 SkipChildren(_suite, Result.ResultState.WithSite(FailureSite.Parent), "OneTimeSetUp: " + Result.Message);
143 // Directly execute the OneTimeFixtureTearDown for tests that
144 // were skipped, failed or set to inconclusive in one time setup
145 // unless we are aborting.
146 if (Context.ExecutionStatus != TestExecutionStatus.AbortRequested)
147 PerformOneTimeTearDown();
151 case RunState.Skipped:
152 SkipFixture(ResultState.Skipped, GetSkipReason(), null);
155 case RunState.Ignored:
156 SkipFixture(ResultState.Ignored, GetSkipReason(), null);
159 case RunState.NotRunnable:
160 SkipFixture(ResultState.NotRunnable, GetSkipReason(), GetProviderStackTrace());
164 // Fall through in case nothing was run.
165 // Otherwise, this is done in the completion event.
170 #region Helper Methods
172 private bool CheckForCancellation()
174 if (Context.ExecutionStatus != TestExecutionStatus.Running)
176 Result.SetResult(ResultState.Cancelled, "Test cancelled by user");
183 private void InitializeSetUpAndTearDownCommands()
185 List<SetUpTearDownItem> setUpTearDownItems = _suite.TypeInfo != null
186 ? CommandBuilder.BuildSetUpTearDownList(_suite.TypeInfo.Type, typeof(OneTimeSetUpAttribute), typeof(OneTimeTearDownAttribute))
187 : new List<SetUpTearDownItem>();
189 var actionItems = new List<TestActionItem>();
190 foreach (ITestAction action in Actions)
192 // Special handling here for ParameterizedMethodSuite is a bit ugly. However,
193 // it is needed because Tests are not supposed to know anything about Action
194 // Attributes (or any attribute) and Attributes don't know where they were
195 // initially applied unless we tell them.
197 // ParameterizedMethodSuites and individual test cases both use the same
198 // MethodInfo as a source of attributes. We handle the Test and Default targets
199 // in the test case, so we don't want to doubly handle it here.
200 bool applyToSuite = (action.Targets & ActionTargets.Suite) == ActionTargets.Suite
201 || action.Targets == ActionTargets.Default && !(Test is ParameterizedMethodSuite);
203 bool applyToTest = (action.Targets & ActionTargets.Test) == ActionTargets.Test
204 && !(Test is ParameterizedMethodSuite);
207 actionItems.Add(new TestActionItem(action));
210 Context.UpstreamActions.Add(action);
213 _setupCommand = CommandBuilder.MakeOneTimeSetUpCommand(_suite, setUpTearDownItems, actionItems);
214 _teardownCommand = CommandBuilder.MakeOneTimeTearDownCommand(_suite, setUpTearDownItems, actionItems);
217 private void PerformOneTimeSetUp()
221 _setupCommand.Execute(Context);
223 // SetUp may have changed some things in the environment
224 Context.UpdateContextFromEnvironment();
228 if (ex is NUnitException || ex is TargetInvocationException)
229 ex = ex.InnerException;
231 Result.RecordException(ex, FailureSite.SetUp);
235 private void RunChildren()
237 int childCount = _children.Count;
239 throw new InvalidOperationException("RunChildren called but item has no children");
241 _childTestCountdown = new CountdownEvent(childCount);
243 foreach (WorkItem child in _children)
245 if (CheckForCancellation())
249 #region tronghieu.d - testkit-stub
251 if (child is CompositeWorkItem || !tsettings.IsManual
252 || child.Test.FullName.Equals(tsettings.Testcase_ID))
256 if (child is SimpleWorkItem && tsettings.IsSlaveMode && !child.Test.FullName.Equals(tsettings.Testcase_ID)) continue;
257 child.Completed += new EventHandler(OnChildCompleted);
258 if (child.Context == null)
260 child.InitializeContext(new TestExecutionContext(Context));
262 Context.Dispatcher.Dispatch(child);
269 while (childCount-- > 0)
270 CountDownChildTest();
274 private void CreateChildWorkItems()
276 _children = new List<WorkItem>();
278 foreach (ITest test in _suite.Tests)
280 if (_childFilter.Pass(test))
282 var child = WorkItem.CreateWorkItem(test, _childFilter);
283 child.WorkerId = this.WorkerId;
285 #if !PORTABLE && !SILVERLIGHT && !NETCF
286 if (child.TargetApartment == ApartmentState.Unknown && TargetApartment != ApartmentState.Unknown)
287 child.TargetApartment = TargetApartment;
290 if (test.Properties.ContainsKey(PropertyNames.Order))
292 _children.Insert(0, child);
297 _children.Add(child);
302 if (_countOrder != 0) SortChildren();
305 private class WorkItemOrderComparer : IComparer<WorkItem>
308 /// Compares two objects and returns a value indicating whether one is less than, equal to, or greater than the other.
311 /// A signed integer that indicates the relative values of <paramref name="x"/> and <paramref name="y"/>, as shown in the following table.Value Meaning Less than zero<paramref name="x"/> is less than <paramref name="y"/>.Zero<paramref name="x"/> equals <paramref name="y"/>.Greater than zero<paramref name="x"/> is greater than <paramref name="y"/>.
313 /// <param name="x">The first object to compare.</param><param name="y">The second object to compare.</param>
314 public int Compare(WorkItem x, WorkItem y)
316 var xKey = int.MaxValue;
317 var yKey = int.MaxValue;
319 if (x.Test.Properties.ContainsKey(PropertyNames.Order))
320 xKey = (int)x.Test.Properties[PropertyNames.Order][0];
322 if (y.Test.Properties.ContainsKey(PropertyNames.Order))
323 yKey = (int)y.Test.Properties[PropertyNames.Order][0];
325 return xKey.CompareTo(yKey);
330 /// Sorts tests under this suite.
332 private void SortChildren()
334 _children.Sort(0, _countOrder, new WorkItemOrderComparer());
337 private void SkipFixture(ResultState resultState, string message, string stackTrace)
339 Result.SetResult(resultState.WithSite(FailureSite.SetUp), message, StackFilter.Filter(stackTrace));
340 SkipChildren(_suite, resultState.WithSite(FailureSite.Parent), "OneTimeSetUp: " + message);
343 private void SkipChildren(TestSuite suite, ResultState resultState, string message)
345 foreach (Test child in suite.Tests)
347 if (_childFilter.Pass(child))
349 TestResult childResult = child.MakeTestResult();
350 childResult.SetResult(resultState, message);
351 _suiteResult.AddResult(childResult);
353 //[tronghieu.d] - return FAIL TC in case it is skipped.
354 if (tsettings.IsSlaveMode && childResult.FullName.Equals(tsettings.Testcase_ID))
356 tsettings.TCResult = "FAIL";
357 tsettings.TCMessage = childResult.Message;
358 string json = "case_id=" + tsettings.Testcase_ID + "&result=" + tsettings.TCResult + "&msg=" + tsettings.TCMessage + "\n" + childResult.StackTrace;
359 if (!tsettings.IsManual)
361 tsettings.RequestPOST("commit_result", json);
362 tsettings.NextStepRequest();
365 tsettings.RequestPOST("commit_manual_result", json);
368 // Some runners may depend on getting the TestFinished event
369 // even for tests that have been skipped at a higher level.
370 Context.Listener.TestFinished(childResult);
373 SkipChildren((TestSuite)child, resultState, message);
378 private void PerformOneTimeTearDown()
380 // Our child tests or even unrelated tests may have
381 // executed on the same thread since the time that
382 // this test started, so we have to re-establish
383 // the proper execution environment
384 this.Context.EstablishExecutionEnvironment();
385 _teardownCommand.Execute(this.Context);
388 private string GetSkipReason()
390 return (string)Test.Properties.Get(PropertyNames.SkipReason);
393 private string GetProviderStackTrace()
395 return (string)Test.Properties.Get(PropertyNames.ProviderStackTrace);
398 private object _completionLock = new object();
400 private void OnChildCompleted(object sender, EventArgs e)
402 lock (_completionLock)
404 WorkItem childTask = sender as WorkItem;
405 if (childTask != null)
407 childTask.Completed -= new EventHandler(OnChildCompleted);
409 _suiteResult.AddResult(childTask.Result);
411 #region tronghieu.d - testkit-stub
412 if (!tsettings.IsSlaveMode && !tsettings.IsManual)
413 _suiteResult.AddResult(childTask.Result);
416 if (!_suiteResult.HasChildren)
417 _suiteResult.AddResult(childTask.Result);
420 ITestResult[] children;
422 var childrenAsConcurrentQueue = _suiteResult.Children as ConcurrentQueue<ITestResult>;
423 if (childrenAsConcurrentQueue != null)
424 children = childrenAsConcurrentQueue.ToArray();
428 var childrenAsIList = Children as IList<ITestResult>;
429 if (childrenAsIList != null)
430 children = (ITestResult[])childrenAsIList;
432 throw new NotSupportedException("cannot add results to Children");
436 for (int i = 0; i < children.Length; i++)
438 if (children[i].FullName.Equals(childTask.Result.FullName))
445 _suiteResult.AddResult(childTask.Result);
447 if (childTask is SimpleWorkItem)
449 string result = childTask.Result.ResultState.Status.ToString();
450 if (tsettings.IsManual && tsettings.TCResult.Length != 0)
456 result = result.Substring(0, result.Length - 2);
457 tsettings.TCResult = result.ToUpper();
459 tsettings.Testcase_ID = childTask.Result.FullName;
460 tsettings.TCMessage = childTask.Result.Message;
461 TLogger.Write("##### Result of [" + tsettings.Testcase_ID + "] TC : " + tsettings.TCResult + " With Message: " + tsettings.TCMessage);
463 if (tsettings.IsSlaveMode)
465 string json = "case_id=" + tsettings.Testcase_ID + "&result=" + tsettings.TCResult + "&msg=" + tsettings.TCMessage + "\n" + childTask.Result.StackTrace;
466 if (!tsettings.IsManual)
468 tsettings.RequestPOST("commit_result", json);
469 tsettings.NextStepRequest();
472 tsettings.RequestPOST("commit_manual_result", json);
479 if (Context.StopOnError && childTask.Result.ResultState.Status == TestStatus.Failed)
480 Context.ExecutionStatus = TestExecutionStatus.StopRequested;
482 // Check to see if all children completed
483 CountDownChildTest();
488 private void CountDownChildTest()
490 _childTestCountdown.Signal();
491 if (_childTestCountdown.CurrentCount == 0)
493 if (Context.ExecutionStatus != TestExecutionStatus.AbortRequested)
494 PerformOneTimeTearDown();
496 foreach (var childResult in _suiteResult.Children)
497 if (childResult.ResultState == ResultState.Cancelled)
499 this.Result.SetResult(ResultState.Cancelled, "Cancelled by user");
506 private static bool IsStaticClass(Type type)
508 return type.GetTypeInfo().IsAbstract && type.GetTypeInfo().IsSealed;
511 private object cancelLock = new object();
514 /// Cancel (abort or stop) a CompositeWorkItem and all of its children
516 /// <param name="force">true if the CompositeWorkItem and all of its children should be aborted, false if it should allow all currently running tests to complete</param>
517 public override void Cancel(bool force)
521 if (_children == null)
524 foreach (var child in _children)
526 var ctx = child.Context;
528 ctx.ExecutionStatus = force ? TestExecutionStatus.AbortRequested : TestExecutionStatus.StopRequested;
530 if (child.State == WorkItemState.Running)
536 private string GetCaseID(string TCID)
538 string[] delimiter = { "." };
539 string[] stringPieces;
540 string ClassName = "";
541 string MethodName = "";
542 stringPieces = TCID.Split(delimiter, StringSplitOptions.None);
543 for (int i = 0; i < stringPieces.Length; i++)
545 if (i == stringPieces.Length - 1)
546 MethodName = stringPieces[i];
547 else if (i == stringPieces.Length - 2)
548 ClassName = stringPieces[i];
551 return ClassName + "." + MethodName;