[NUI] Rebase develnui (DevelNUI only patches --> master) (#3910)
[platform/core/csapi/tizenfx.git] / test / Tizen.NUI.Devel.Tests.Ubuntu / nunit.framework / Internal / Execution / WorkItem.cs
1 // ***********************************************************************
2 // Copyright (c) 2012 Charlie Poole
3 //
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:
11 //
12 // The above copyright notice and this permission notice shall be
13 // included in all copies or substantial portions of the Software.
14 //
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 // ***********************************************************************
23 #define PORTABLE
24 #define TIZEN
25 #define NUNIT_FRAMEWORK
26 #define NUNITLITE
27 #define NET_4_5
28 #define PARALLEL
29 using System;
30 using System.Collections.Generic;
31 using System.Diagnostics;
32 using System.Threading;
33 using NUnit.Framework.Interfaces;
34
35 namespace NUnit.Framework.Internal.Execution
36 {
37     /// <summary>
38     /// A WorkItem may be an individual test case, a fixture or
39     /// a higher level grouping of tests. All WorkItems inherit
40     /// from the abstract WorkItem class, which uses the template
41     /// pattern to allow derived classes to perform work in
42     /// whatever way is needed.
43     ///
44     /// A WorkItem is created with a particular TestExecutionContext
45     /// and is responsible for re-establishing that context in the
46     /// current thread before it begins or resumes execution.
47     /// </summary>
48     public abstract class WorkItem
49     {
50         static Logger log = InternalTrace.GetLogger("WorkItem");
51
52         #region Static Factory Method
53
54         /// <summary>
55         /// Creates a work item.
56         /// </summary>
57         /// <param name="test">The test for which this WorkItem is being created.</param>
58         /// <param name="filter">The filter to be used in selecting any child Tests.</param>
59         /// <returns></returns>
60         static public WorkItem CreateWorkItem(ITest test, ITestFilter filter)
61         {
62             TestSuite suite = test as TestSuite;
63             if (suite != null)
64                 return new CompositeWorkItem(suite, filter);
65             else
66                 return new SimpleWorkItem((TestMethod)test, filter);
67         }
68
69         #endregion
70
71         #region Construction and Initialization
72
73         /// <summary>
74         /// Construct a WorkItem for a particular test.
75         /// </summary>
76         /// <param name="test">The test that the WorkItem will run</param>
77         public WorkItem(Test test)
78         {
79             Test = test;
80             Result = test.MakeTestResult();
81             State = WorkItemState.Ready;
82             Actions = new List<ITestAction>();
83 #if !PORTABLE && !SILVERLIGHT && !NETCF
84             TargetApartment = Test.Properties.ContainsKey(PropertyNames.ApartmentState)
85                 ? (ApartmentState)Test.Properties.Get(PropertyNames.ApartmentState)
86                 : ApartmentState.Unknown;
87 #endif
88         }
89
90         /// <summary>
91         /// Initialize the TestExecutionContext. This must be done
92         /// before executing the WorkItem.
93         /// </summary>
94         /// <remarks>
95         /// Originally, the context was provided in the constructor
96         /// but delaying initialization of the context until the item
97         /// is about to be dispatched allows changes in the parent
98         /// context during OneTimeSetUp to be reflected in the child.
99         /// </remarks>
100         /// <param name="context">The TestExecutionContext to use</param>
101         public void InitializeContext(TestExecutionContext context)
102         {
103             Guard.OperationValid(Context == null, "The context has already been initialized");
104
105             Context = context;
106
107             //if (Test is TestAssembly)
108             //    Actions.AddRange(ActionsHelper.GetActionsFromAttributeProvider(((TestAssembly)Test).Assembly));
109             //else if (Test is ParameterizedMethodSuite)
110             //    Actions.AddRange(ActionsHelper.GetActionsFromAttributeProvider(Test.Method.MethodInfo));
111             //else if (Test.TypeInfo != null)
112             //    Actions.AddRange(ActionsHelper.GetActionsFromTypesAttributes(Test.TypeInfo.Type));
113         }
114
115         #endregion
116
117         #region Properties and Events
118
119         /// <summary>
120         /// Event triggered when the item is complete
121         /// </summary>
122         public event EventHandler Completed;
123
124         /// <summary>
125         /// Gets the current state of the WorkItem
126         /// </summary>
127         public WorkItemState State { get; private set; }
128
129         /// <summary>
130         /// The test being executed by the work item
131         /// </summary>
132         public Test Test { get; private set; }
133
134         /// <summary>
135         /// The execution context
136         /// </summary>
137         public TestExecutionContext Context { get; private set; }
138
139         /// <summary>
140         /// The unique id of the worker executing this item.
141         /// </summary>
142         public string WorkerId {get; internal set;}
143
144         /// <summary>
145         /// The test actions to be performed before and after this test
146         /// </summary>
147         public List<ITestAction> Actions { get; private set; }
148
149 #if PARALLEL
150         /// <summary>
151         /// Indicates whether this WorkItem may be run in parallel
152         /// </summary>
153         public bool IsParallelizable
154         {
155             get
156             {
157                 ParallelScope scope = ParallelScope.None;
158
159                 if (Test.Properties.ContainsKey(PropertyNames.ParallelScope))
160                 {
161                     scope = (ParallelScope)Test.Properties.Get(PropertyNames.ParallelScope);
162
163                     if ((scope & ParallelScope.Self) != 0)
164                         return true;
165                 }
166                 else
167                 {
168                     scope = Context.ParallelScope;
169
170                     if ((scope & ParallelScope.Children) != 0)
171                         return true;
172                 }
173
174                 if (Test is TestFixture && (scope & ParallelScope.Fixtures) != 0)
175                     return true;
176
177                 // Special handling for the top level TestAssembly.
178                 // If it has any scope specified other than None,
179                 // we will use the parallel queue. This heuristic
180                 // is intended to minimize creation of unneeded
181                 // queues and workers, since the assembly and
182                 // namespace level tests can easily run in any queue.
183                 if (Test is TestAssembly && scope != ParallelScope.None)
184                     return true;
185
186                 return false;
187             }
188         }
189 #endif
190
191         /// <summary>
192         /// The test result
193         /// </summary>
194         public TestResult Result { get; protected set; }
195
196 #if !SILVERLIGHT && !NETCF && !PORTABLE
197         internal ApartmentState TargetApartment { get; set; }
198         private ApartmentState CurrentApartment { get; set; }
199 #endif
200
201         #endregion
202
203         #region OwnThreadReason Enumeration
204
205         [Flags]
206         private enum OwnThreadReason
207         {
208             NotNeeded = 0,
209             RequiresThread = 1,
210             Timeout = 2,
211             DifferentApartment = 4
212         }
213
214         #endregion
215
216         #region Public Methods
217
218         /// <summary>
219         /// Execute the current work item, including any
220         /// child work items.
221         /// </summary>
222         public virtual void Execute()
223         {
224             // Timeout set at a higher level
225             int timeout = Context.TestCaseTimeout;
226
227             // Timeout set on this test
228             if (Test.Properties.ContainsKey(PropertyNames.Timeout))
229                 timeout = (int)Test.Properties.Get(PropertyNames.Timeout);
230
231             // Unless the context is single threaded, a supplementary thread 
232             // is created on the various platforms...
233             // 1. If the test used the RequiresThreadAttribute.
234             // 2. If a test method has a timeout.
235             // 3. If the test needs to run in a different apartment.
236             //
237             // NOTE: We want to eliminate or significantly reduce 
238             //       cases 2 and 3 in the future.
239             //
240             // Case 2 requires the ability to stop and start test workers
241             // dynamically. We would cancel the worker thread, dispose of
242             // the worker and start a new worker on a new thread.
243             //
244             // Case 3 occurs when using either dispatcher whenever a
245             // child test calls for a different apartment from the one
246             // used by it's parent. It routinely occurs under the simple
247             // dispatcher (--workers=0 option). Under the parallel dispatcher
248             // it is needed when test cases are not enabled for parallel
249             // execution. Currently, test cases are always run sequentially,
250             // so this continues to apply fairly generally.
251
252             var ownThreadReason = OwnThreadReason.NotNeeded;
253
254 #if !PORTABLE
255             if (Test.RequiresThread)
256                 ownThreadReason |= OwnThreadReason.RequiresThread;
257             if (timeout > 0 && Test is TestMethod)
258                 ownThreadReason |= OwnThreadReason.Timeout;
259 #if !SILVERLIGHT && !NETCF
260             CurrentApartment = Thread.CurrentThread.GetApartmentState();
261             if (CurrentApartment != TargetApartment && TargetApartment != ApartmentState.Unknown)
262                 ownThreadReason |= OwnThreadReason.DifferentApartment;
263 #endif
264 #endif
265
266             if (ownThreadReason == OwnThreadReason.NotNeeded)
267                 RunTest();
268             else if (Context.IsSingleThreaded)
269             {
270                 var msg = "Test is not runnable in single-threaded context. " + ownThreadReason;
271                 log.Error(msg);
272                 Result.SetResult(ResultState.NotRunnable, msg);
273                 WorkItemComplete();
274             }
275             else
276             {
277                 log.Debug("Running test on own thread. " + ownThreadReason);
278 #if SILVERLIGHT || NETCF
279                 RunTestOnOwnThread(timeout);
280 #elif !PORTABLE
281                 var apartment = (ownThreadReason | OwnThreadReason.DifferentApartment) != 0
282                     ? TargetApartment
283                     : CurrentApartment;
284                 RunTestOnOwnThread(timeout, apartment);
285 #endif
286             }
287         }
288
289 #if SILVERLIGHT || NETCF
290         private Thread thread;
291
292         private void RunTestOnOwnThread(int timeout)
293         {
294             thread = new Thread(RunTest);
295             RunThread(timeout);
296         }
297 #endif
298
299 #if !SILVERLIGHT && !NETCF && !PORTABLE
300         private Thread thread;
301
302         private void RunTestOnOwnThread(int timeout, ApartmentState apartment)
303         {
304             thread = new Thread(new ThreadStart(RunTest));
305             thread.SetApartmentState(apartment);
306             RunThread(timeout);
307         }
308 #endif
309
310 #if !PORTABLE
311         private void RunThread(int timeout)
312         {
313 #if !NETCF
314             thread.CurrentCulture = Context.CurrentCulture;
315             thread.CurrentUICulture = Context.CurrentUICulture;
316 #endif
317
318             thread.Start();
319
320             if (!Test.IsAsynchronous || timeout > 0)
321             {
322                 if (timeout <= 0)
323                     timeout = Timeout.Infinite;
324
325                 if (!thread.Join(timeout))
326                 {
327                     Thread tThread;
328                     lock (threadLock)
329                     {
330                         if (thread == null)
331                             return;
332
333                         tThread = thread;
334                         thread = null;
335                     }
336
337                     if (Context.ExecutionStatus == TestExecutionStatus.AbortRequested)
338                         return;
339
340                     log.Debug("Killing thread {0}, which exceeded timeout", tThread.ManagedThreadId);
341                     ThreadUtility.Kill(tThread);
342
343                     // NOTE: Without the use of Join, there is a race condition here.
344                     // The thread sets the result to Cancelled and our code below sets
345                     // it to Failure. In order for the result to be shown as a failure,
346                     // we need to ensure that the following code executes after the
347                     // thread has terminated. There is a risk here: the test code might
348                     // refuse to terminate. However, it's more important to deal with
349                     // the normal rather than a pathological case.
350                     tThread.Join();
351
352                     log.Debug("Changing result from {0} to Timeout Failure", Result.ResultState);
353
354                     Result.SetResult(ResultState.Failure,
355                         string.Format("Test exceeded Timeout value of {0}ms", timeout));
356
357                     WorkItemComplete();
358                 }
359             }
360         }
361 #endif
362
363         private void RunTest()
364         {
365             Context.CurrentTest = this.Test;
366             Context.CurrentResult = this.Result;
367             Context.Listener.TestStarted(this.Test);
368             Context.StartTime = DateTime.UtcNow;
369             Context.StartTicks = Stopwatch.GetTimestamp();
370             Context.WorkerId = this.WorkerId;
371             Context.EstablishExecutionEnvironment();
372
373             State = WorkItemState.Running;
374
375             PerformWork();
376         }
377
378         private object threadLock = new object();
379
380         /// <summary>
381         /// Cancel (abort or stop) a WorkItem
382         /// </summary>
383         /// <param name="force">true if the WorkItem should be aborted, false if it should run to completion</param>
384         public virtual void Cancel(bool force)
385         {
386             if (Context != null)
387                 Context.ExecutionStatus = force ? TestExecutionStatus.AbortRequested : TestExecutionStatus.StopRequested;
388
389             if (!force)
390                 return;
391
392 #if !PORTABLE
393             Thread tThread;
394
395             lock (threadLock)
396             {
397                 if (thread == null)
398                     return;
399
400                 tThread = thread;
401                 thread = null;
402             }
403
404             if (!tThread.Join(0))
405             {
406                 log.Debug("Killing thread {0} for cancel", tThread.ManagedThreadId);
407                 ThreadUtility.Kill(tThread);
408
409                 tThread.Join();
410
411                 log.Debug("Changing result from {0} to Cancelled", Result.ResultState);
412
413                 Result.SetResult(ResultState.Cancelled, "Cancelled by user");
414
415                 WorkItemComplete();
416             }
417 #endif
418         }
419
420 #endregion
421
422 #region Protected Methods
423
424         /// <summary>
425         /// Method that performs actually performs the work. It should
426         /// set the State to WorkItemState.Complete when done.
427         /// </summary>
428         protected abstract void PerformWork();
429
430         /// <summary>
431         /// Method called by the derived class when all work is complete
432         /// </summary>
433         protected void WorkItemComplete()
434         {
435             State = WorkItemState.Complete;
436
437             Result.StartTime = Context.StartTime;
438             Result.EndTime = DateTime.UtcNow;
439
440             long tickCount = Stopwatch.GetTimestamp() - Context.StartTicks;
441             double seconds = (double)tickCount / Stopwatch.Frequency;
442             Result.Duration = seconds;
443
444             // We add in the assert count from the context. If
445             // this item is for a test case, we are adding the
446             // test assert count to zero. If it's a fixture, we
447             // are adding in any asserts that were run in the
448             // fixture setup or teardown. Each context only
449             // counts the asserts taking place in that context.
450             // Each result accumulates the count from child
451             // results along with it's own asserts.
452             Result.AssertCount += Context.AssertCount;
453
454             Context.Listener.TestFinished(Result);
455
456             if (Completed != null)
457                 Completed(this, EventArgs.Empty);
458
459             //Clear references to test objects to reduce memory usage
460             Context.TestObject = null;
461             Test.Fixture = null;
462         }
463
464 #endregion
465     }
466 }