1 // ***********************************************************************
2 // Copyright (c) 2012-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;
32 using System.Reflection;
33 using System.Threading;
35 using NUnit.Framework.Interfaces;
36 using NUnit.Framework.Internal;
37 using NUnit.Framework.TUnit;
38 using NUnit.Framework.Internal.Execution;
39 using System.Collections.Generic;
41 #if !SILVERLIGHT && !NETCF && !PORTABLE
42 using System.Diagnostics;
43 //using System.Windows.Forms;
46 namespace NUnit.Framework.Api
49 /// Implementation of ITestAssemblyRunner
51 public class NUnitTestAssemblyRunner : ITestAssemblyRunner
53 private static Logger log = InternalTrace.GetLogger("DefaultTestAssemblyRunner");
55 private ITestAssemblyBuilder _builder;
56 private ManualResetEvent _runComplete = new ManualResetEvent(false);
58 private ITestListener _listener;
60 private Dictionary<string, ITest> TestcaseIDDic;
63 #if !SILVERLIGHT && !NETCF && !PORTABLE
64 // Saved Console.Out and Console.Error
65 private TextWriter _savedOut;
66 private TextWriter _savedErr;
71 private EventPump _pump;
77 /// Initializes a new instance of the <see cref="NUnitTestAssemblyRunner"/> class.
79 /// <param name="builder">The builder.</param>
80 public NUnitTestAssemblyRunner(ITestAssemblyBuilder builder)
84 TestcaseIDDic = new Dictionary<string, ITest>();
94 /// Gets the default level of parallel execution (worker threads)
96 public static int DefaultLevelOfParallelism
101 get { return Math.Max(Environment.ProcessorCount, 2); }
107 /// The tree of tests that was loaded by the builder
109 public ITest LoadedTest { get; private set; }
112 /// The test result, if a run has completed
114 public ITestResult Result
116 get { return TopLevelWorkItem == null ? null : TopLevelWorkItem.Result; }
120 /// Indicates whether a test is loaded
122 public bool IsTestLoaded
124 get { return LoadedTest != null; }
128 /// Indicates whether a test is running
130 public bool IsTestRunning
132 get { return TopLevelWorkItem != null && TopLevelWorkItem.State == WorkItemState.Running; }
136 /// Indicates whether a test run is complete
138 public bool IsTestComplete
140 get { return TopLevelWorkItem != null && TopLevelWorkItem.State == WorkItemState.Complete; }
144 /// Our settings, specified when loading the assembly
146 private IDictionary<string, object> Settings { get; set; }
149 /// The top level WorkItem created for the assembly as a whole
151 private WorkItem TopLevelWorkItem { get; set; }
154 /// The TestExecutionContext for the top level WorkItem
156 private TestExecutionContext Context { get; set; }
160 #region Public Methods
163 /// Loads the tests found in an Assembly
165 /// <param name="assemblyName">File name of the assembly to load</param>
166 /// <param name="settings">Dictionary of option settings for loading the assembly</param>
167 /// <returns>True if the load was successful</returns>
168 public ITest Load(string assemblyName, IDictionary<string, object> settings)
172 if (settings.ContainsKey(PackageSettings.RandomSeed))
173 Randomizer.InitialSeed = (int)settings[PackageSettings.RandomSeed];
175 return LoadedTest = _builder.Build(assemblyName, settings);
180 /// Loads the tests found in an Assembly
182 /// <param name="assembly">The assembly to load</param>
183 /// <param name="settings">Dictionary of option settings for loading the assembly</param>
184 /// <returns>True if the load was successful</returns>
185 public ITest Load(Assembly assembly, IDictionary<string, object> settings)
189 if (settings.ContainsKey(PackageSettings.RandomSeed))
190 Randomizer.InitialSeed = (int)settings[PackageSettings.RandomSeed];
192 LoadedTest = _builder.Build(assembly, settings);
193 MakeTestcaseIDDic(LoadedTest);
199 /// Make Testcase ID Dictionary
200 public void MakeTestcaseIDDic(ITest testsuite)
202 foreach (ITest testmethod in testsuite.Tests)
204 if (testmethod.IsSuite)
206 MakeTestcaseIDDic(testmethod);
210 TestcaseIDDic.Add(testmethod.FullName, testmethod);
216 /// Get Testcase ID List
218 /// <returns>Return Dictionary of TestcaseID</returns>
219 public Dictionary<string, ITest> GetTestcaseIDList()
221 return TestcaseIDDic;
226 /// Count Test Cases using a filter
228 /// <param name="filter">The filter to apply</param>
229 /// <returns>The number of test cases found</returns>
230 public int CountTestCases(ITestFilter filter)
232 if (LoadedTest == null)
233 throw new InvalidOperationException("The CountTestCases method was called but no test has been loaded");
235 return CountTestCases(LoadedTest, filter);
239 /// Run selected tests and return a test result. The test is run synchronously,
240 /// and the listener interface is notified as it progresses.
242 /// <param name="listener">Interface to receive EventListener notifications.</param>
243 /// <param name="filter">A test filter used to select tests to be run</param>
244 /// <returns></returns>
245 public ITestResult Run(ITestListener listener, ITestFilter filter)
247 _listener = listener;
248 RunAsync(listener, filter);
254 /// Run selected tests asynchronously, notifying the listener interface as it progresses.
256 /// <param name="listener">Interface to receive EventListener notifications.</param>
257 /// <param name="filter">A test filter used to select tests to be run</param>
259 /// RunAsync is a template method, calling various abstract and
260 /// virtual methods to be overridden by derived classes.
262 public void RunAsync(ITestListener listener, ITestFilter filter)
264 log.Info("Running tests");
265 if (LoadedTest == null)
266 throw new InvalidOperationException("The Run method was called but no test has been loaded");
268 _runComplete.Reset();
270 CreateTestExecutionContext(listener);
272 TopLevelWorkItem = WorkItem.CreateWorkItem(LoadedTest, filter);
273 TopLevelWorkItem.InitializeContext(Context);
274 TopLevelWorkItem.Completed += OnRunCompleted;
277 TSettings tsetting = TSettings.GetInstance();
278 if (tsetting.IsSlaveMode && !tsetting.IsManual)
280 tsetting.NextStepRequest();
282 if (tsetting.Testcase_ID.Equals("0") || TSettings.IsLastTC == true)
290 WaitForCompletion(Timeout.Infinite);
294 /// Wait for the ongoing run to complete.
296 /// <param name="timeout">Time to wait in milliseconds</param>
297 /// <returns>True if the run completed, otherwise false</returns>
298 public bool WaitForCompletion(int timeout)
300 #if !SILVERLIGHT && !PORTABLE
301 return _runComplete.WaitOne(timeout, false);
303 return _runComplete.WaitOne(timeout);
308 /// Signal any test run that is in process to stop. Return without error if no test is running.
310 /// <param name="force">If true, kill any tests that are currently running</param>
311 public void StopRun(bool force)
315 Context.ExecutionStatus = force
316 ? TestExecutionStatus.AbortRequested
317 : TestExecutionStatus.StopRequested;
319 Context.Dispatcher.CancelRun(force);
325 #region Helper Methods
328 /// Initiate the test run.
330 private void StartRun(ITestListener listener)
332 #if !SILVERLIGHT && !NETCF && !PORTABLE
333 // Save Console.Out and Error for later restoration
334 _savedOut = Console.Out;
335 _savedErr = Console.Error;
337 Console.SetOut(new TextCapture(Console.Out));
338 Console.SetError(new EventListenerTextWriter("Error", Console.Error));
342 // Queue and pump events, unless settings have SynchronousEvents == false
343 if (!Settings.ContainsKey(PackageSettings.SynchronousEvents) || !(bool)Settings[PackageSettings.SynchronousEvents])
345 QueuingEventListener queue = new QueuingEventListener();
346 Context.Listener = queue;
348 _pump = new EventPump(listener, queue.Events);
354 if (!System.Diagnostics.Debugger.IsAttached &&
355 Settings.ContainsKey(PackageSettings.DebugTests) &&
356 (bool)Settings[PackageSettings.DebugTests])
357 System.Diagnostics.Debugger.Launch();
359 #if !SILVERLIGHT && !PORTABLE
360 if (Settings.ContainsKey(PackageSettings.PauseBeforeRun) &&
361 (bool)Settings[PackageSettings.PauseBeforeRun])
367 Context.Dispatcher.Dispatch(TopLevelWorkItem);
371 /// Create the initial TestExecutionContext used to run tests
373 /// <param name="listener">The ITestListener specified in the RunAsync call</param>
374 private void CreateTestExecutionContext(ITestListener listener)
376 Context = new TestExecutionContext();
378 // Apply package settings to the context
379 if (Settings.ContainsKey(PackageSettings.DefaultTimeout))
380 Context.TestCaseTimeout = (int)Settings[PackageSettings.DefaultTimeout];
381 if (Settings.ContainsKey(PackageSettings.StopOnError))
382 Context.StopOnError = (bool)Settings[PackageSettings.StopOnError];
384 if (Settings.ContainsKey(PackageSettings.WorkDirectory))
385 Context.WorkDirectory = (string)Settings[PackageSettings.WorkDirectory];
387 Context.WorkDirectory = Env.DefaultWorkDirectory;
389 // Apply attributes to the context
391 // Set the listener - overriding runners may replace this
392 Context.Listener = listener;
395 int levelOfParallelism = GetLevelOfParallelism();
397 if (levelOfParallelism > 0)
398 Context.Dispatcher = new ParallelWorkItemDispatcher(levelOfParallelism);
400 Context.Dispatcher = new SimpleWorkItemDispatcher();
402 Context.Dispatcher = new SimpleWorkItemDispatcher();
407 /// Handle the the Completed event for the top level work item
409 private void OnRunCompleted(object sender, EventArgs e)
416 #if !SILVERLIGHT && !NETCF && !PORTABLE
417 Console.SetOut(_savedOut);
418 Console.SetError(_savedErr);
421 #region tronghieu.d - testkit-stub
422 TSettings tsetting = TSettings.GetInstance();
423 if (tsetting.IsSlaveMode && !tsetting.IsManual)
425 //tsetting.NextStepRequest();
427 if (tsetting.Testcase_ID != "0" && TSettings.IsLastTC != true)
439 private int CountTestCases(ITest test, ITestFilter filter)
445 foreach (ITest child in test.Tests)
446 if (filter.Pass(child))
447 count += CountTestCases(child, filter);
453 private int GetLevelOfParallelism()
455 return Settings.ContainsKey(PackageSettings.NumberOfTestWorkers)
456 ? (int)Settings[PackageSettings.NumberOfTestWorkers]
457 : (LoadedTest.Properties.ContainsKey(PropertyNames.LevelOfParallelism)
458 ? (int)LoadedTest.Properties.Get(PropertyNames.LevelOfParallelism)
459 : NUnitTestAssemblyRunner.DefaultLevelOfParallelism);
463 #if !SILVERLIGHT && !NETCF && !PORTABLE
464 private static void PauseBeforeRun()
466 var process = Process.GetCurrentProcess();
467 string attachMessage = string.Format("Attach debugger to Process {0}.exe with Id {1} if desired.", process.ProcessName, process.Id);
468 //MessageBox.Show(attachMessage, process.ProcessName, MessageBoxButtons.OK, MessageBoxIcon.Information);