1 // ***********************************************************************
2 // Copyright (c) 2014 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.Globalization;
32 using System.Diagnostics;
34 using System.Threading;
35 using NUnit.Framework.Constraints;
36 using NUnit.Framework.Interfaces;
37 using NUnit.Framework.Internal.Execution;
39 #if !SILVERLIGHT && !NETCF && !PORTABLE
40 using System.Runtime.Remoting.Messaging;
41 using System.Security.Principal;
42 using NUnit.Compatibility;
45 namespace NUnit.Framework.Internal
48 /// Helper class used to save and restore certain static or
49 /// singleton settings in the environment that affect tests
50 /// or which might be changed by the user tests.
52 /// An internal class is used to hold settings and a stack
53 /// of these objects is pushed and popped as Save and Restore
56 public class TestExecutionContext
57 #if !SILVERLIGHT && !NETCF && !PORTABLE
58 : LongLivedMarshalByRefObject, ILogicalThreadAffinative
61 // NOTE: Be very careful when modifying this class. It uses
62 // conditional compilation extensively and you must give
63 // thought to whether any new features will be supported
64 // on each platform. In particular, instance fields,
65 // properties, initialization and restoration must all
66 // use the same conditions for each feature.
68 #region Instance Fields
71 /// Link to a prior saved context
73 private TestExecutionContext _priorContext;
76 /// Indicates that a stop has been requested
78 private TestExecutionStatus _executionStatus;
81 /// The event listener currently receiving notifications
83 private ITestListener _listener = TestListener.NULL;
86 /// The number of assertions for the current test
88 private int _assertCount;
90 private Randomizer _randomGenerator;
93 /// The current culture
95 private CultureInfo _currentCulture;
98 /// The current UI culture
100 private CultureInfo _currentUICulture;
103 /// The current test result
105 private TestResult _currentResult;
107 #if !NETCF && !SILVERLIGHT && !PORTABLE
109 /// The current Principal.
111 private IPrincipal _currentPrincipal;
119 /// Initializes a new instance of the <see cref="TestExecutionContext"/> class.
121 public TestExecutionContext()
123 _priorContext = null;
125 UpstreamActions = new List<ITestAction>();
127 _currentCulture = CultureInfo.CurrentCulture;
128 _currentUICulture = CultureInfo.CurrentUICulture;
130 #if !NETCF && !SILVERLIGHT && !PORTABLE
131 _currentPrincipal = Thread.CurrentPrincipal;
134 CurrentValueFormatter = (val) => MsgUtils.DefaultValueFormatter(val);
135 IsSingleThreaded = false;
139 /// Initializes a new instance of the <see cref="TestExecutionContext"/> class.
141 /// <param name="other">An existing instance of TestExecutionContext.</param>
142 public TestExecutionContext(TestExecutionContext other)
144 _priorContext = other;
146 CurrentTest = other.CurrentTest;
147 CurrentResult = other.CurrentResult;
148 TestObject = other.TestObject;
149 WorkDirectory = other.WorkDirectory;
150 _listener = other._listener;
151 StopOnError = other.StopOnError;
152 TestCaseTimeout = other.TestCaseTimeout;
153 UpstreamActions = new List<ITestAction>(other.UpstreamActions);
155 _currentCulture = other.CurrentCulture;
156 _currentUICulture = other.CurrentUICulture;
158 #if !NETCF && !SILVERLIGHT && !PORTABLE
159 _currentPrincipal = other.CurrentPrincipal;
162 CurrentValueFormatter = other.CurrentValueFormatter;
164 Dispatcher = other.Dispatcher;
165 ParallelScope = other.ParallelScope;
166 IsSingleThreaded = other.IsSingleThreaded;
171 #region Static Singleton Instance
173 // NOTE: We use different implementations for various platforms
175 // If a user creates a thread then the current context
176 // will be null. This also happens when the compiler
177 // automatically creates threads for async methods.
178 // We create a new context, which is automatically
179 // populated with values taken from the current thread.
181 #if SILVERLIGHT || PORTABLE
182 // In the Silverlight and portable builds, we use a ThreadStatic
183 // field to hold the current TestExecutionContext.
186 private static TestExecutionContext _currentContext;
189 /// Gets and sets the current context.
191 public static TestExecutionContext CurrentContext
195 if (_currentContext == null)
196 _currentContext = new TestExecutionContext();
198 return _currentContext;
202 _currentContext = value;
206 // In the compact framework build, we use a LocalStoreDataSlot
208 private static LocalDataStoreSlot contextSlot = Thread.AllocateDataSlot();
211 /// Gets and sets the current context.
213 public static TestExecutionContext CurrentContext
217 var current = GetTestExecutionContext();
220 current = new TestExecutionContext();
221 Thread.SetData(contextSlot, current);
228 Thread.SetData(contextSlot, value);
233 /// Get the current context or return null if none is found.
235 public static TestExecutionContext GetTestExecutionContext()
237 return (TestExecutionContext)Thread.GetData(contextSlot);
240 // In all other builds, we use the CallContext
242 private static readonly string CONTEXT_KEY = "NUnit.Framework.TestContext";
245 /// Gets and sets the current context.
247 public static TestExecutionContext CurrentContext
251 var context = GetTestExecutionContext();
252 if (context == null) // This can happen on Mono
254 context = new TestExecutionContext();
255 CallContext.SetData(CONTEXT_KEY, context);
263 CallContext.FreeNamedDataSlot(CONTEXT_KEY);
265 CallContext.SetData(CONTEXT_KEY, value);
270 /// Get the current context or return null if none is found.
272 public static TestExecutionContext GetTestExecutionContext()
274 return CallContext.GetData(CONTEXT_KEY) as TestExecutionContext;
279 /// Clear the current context. This is provided to
280 /// prevent "leakage" of the CallContext containing
281 /// the current context back to any runners.
283 public static void ClearCurrentContext()
285 CurrentContext = null;
293 /// Gets or sets the current test
295 public Test CurrentTest { get; set; }
298 /// The time the current test started execution
300 public DateTime StartTime { get; set; }
303 /// The time the current test started in Ticks
305 public long StartTicks { get; set; }
308 /// Gets or sets the current test result
310 public TestResult CurrentResult
312 get { return _currentResult; }
315 _currentResult = value;
317 OutWriter = value.OutWriter;
322 /// Gets a TextWriter that will send output to the current test result.
324 public TextWriter OutWriter { get; private set; }
327 /// The current test object - that is the user fixture
328 /// object on which tests are being executed.
330 public object TestObject { get; set; }
333 /// Get or set the working directory
335 public string WorkDirectory { get; set; }
338 /// Get or set indicator that run should stop on the first error
340 public bool StopOnError { get; set; }
343 /// Gets an enum indicating whether a stop has been requested.
345 public TestExecutionStatus ExecutionStatus
349 // ExecutionStatus may have been set to StopRequested or AbortRequested
350 // in a prior context. If so, reflect the same setting in this context.
351 if (_executionStatus == TestExecutionStatus.Running && _priorContext != null)
352 _executionStatus = _priorContext.ExecutionStatus;
354 return _executionStatus;
358 _executionStatus = value;
360 // Push the same setting up to all prior contexts
361 if (_priorContext != null)
362 _priorContext.ExecutionStatus = value;
367 /// The current test event listener
369 internal ITestListener Listener
371 get { return _listener; }
372 set { _listener = value; }
376 /// The current WorkItemDispatcher. Made public for
377 /// use by nunitlite.tests
379 public IWorkItemDispatcher Dispatcher { get; set; }
382 /// The ParallelScope to be used by tests running in this context.
383 /// For builds with out the parallel feature, it has no effect.
385 public ParallelScope ParallelScope { get; set; }
388 /// The unique name of the worker that spawned the context.
389 /// For builds with out the parallel feature, it is null.
391 public string WorkerId {get; internal set;}
394 /// Gets the RandomGenerator specific to this Test
396 public Randomizer RandomGenerator
400 if (_randomGenerator == null)
401 _randomGenerator = new Randomizer(CurrentTest.Seed);
402 return _randomGenerator;
407 /// Gets the assert count.
409 /// <value>The assert count.</value>
410 internal int AssertCount
412 get { return _assertCount; }
416 /// Gets or sets the test case timeout value
418 public int TestCaseTimeout { get; set; }
421 /// Gets a list of ITestActions set by upstream tests
423 public List<ITestAction> UpstreamActions { get; private set; }
425 // TODO: Put in checks on all of these settings
426 // with side effects so we only change them
427 // if the value is different
430 /// Saves or restores the CurrentCulture
432 public CultureInfo CurrentCulture
434 get { return _currentCulture; }
437 _currentCulture = value;
438 #if !NETCF && !PORTABLE
439 Thread.CurrentThread.CurrentCulture = _currentCulture;
445 /// Saves or restores the CurrentUICulture
447 public CultureInfo CurrentUICulture
449 get { return _currentUICulture; }
452 _currentUICulture = value;
453 #if !NETCF && !PORTABLE
454 Thread.CurrentThread.CurrentUICulture = _currentUICulture;
459 #if !NETCF && !SILVERLIGHT && !PORTABLE
461 /// Gets or sets the current <see cref="IPrincipal"/> for the Thread.
463 public IPrincipal CurrentPrincipal
465 get { return _currentPrincipal; }
468 _currentPrincipal = value;
469 Thread.CurrentPrincipal = _currentPrincipal;
475 /// The current head of the ValueFormatter chain, copied from MsgUtils.ValueFormatter
477 public ValueFormatter CurrentValueFormatter { get; private set; }
480 /// If true, all tests must run on the same thread. No new thread may be spawned.
482 public bool IsSingleThreaded { get; set; }
486 #region Instance Methods
489 /// Record any changes in the environment made by
490 /// the test code in the execution context so it
491 /// will be passed on to lower level tests.
493 public void UpdateContextFromEnvironment()
495 _currentCulture = CultureInfo.CurrentCulture;
496 _currentUICulture = CultureInfo.CurrentUICulture;
498 #if !NETCF && !SILVERLIGHT && !PORTABLE
499 _currentPrincipal = Thread.CurrentPrincipal;
504 /// Set up the execution environment to match a context.
505 /// Note that we may be running on the same thread where the
506 /// context was initially created or on a different thread.
508 public void EstablishExecutionEnvironment()
510 #if !NETCF && !PORTABLE
511 Thread.CurrentThread.CurrentCulture = _currentCulture;
512 Thread.CurrentThread.CurrentUICulture = _currentUICulture;
515 #if !NETCF && !SILVERLIGHT && !PORTABLE
516 Thread.CurrentPrincipal = _currentPrincipal;
519 CurrentContext = this;
523 /// Increments the assert count by one.
525 public void IncrementAssertCount()
527 Interlocked.Increment(ref _assertCount);
531 /// Increments the assert count by a specified amount.
533 public void IncrementAssertCount(int count)
535 // TODO: Temporary implementation
537 Interlocked.Increment(ref _assertCount);
541 /// Adds a new ValueFormatterFactory to the chain of formatters
543 /// <param name="formatterFactory">The new factory</param>
544 public void AddFormatter(ValueFormatterFactory formatterFactory)
546 CurrentValueFormatter = formatterFactory(CurrentValueFormatter);
551 #region InitializeLifetimeService
553 #if !SILVERLIGHT && !NETCF && !PORTABLE
555 /// Obtain lifetime service object
557 /// <returns></returns>
558 public override object InitializeLifetimeService()