PageRenderTime 38ms CodeModel.GetById 12ms RepoModel.GetById 1ms app.codeStats 0ms

/src/NUnit/util/AggregatingTestRunner.cs

#
C# | 453 lines | 323 code | 82 blank | 48 comment | 40 complexity | 5d47f6a52c08813f528ab18a445b7969 MD5 | raw file
Possible License(s): GPL-2.0
  1. // ****************************************************************
  2. // Copyright 2007, Charlie Poole
  3. // This is free software licensed under the NUnit license. You may
  4. // obtain a copy of the license at http://nunit.org
  5. // ****************************************************************
  6. namespace NUnit.Util
  7. {
  8. using System;
  9. using System.Collections;
  10. using System.IO;
  11. using NUnit.Core;
  12. #region AggregatingTestRunner
  13. /// <summary>
  14. /// AggregatingTestRunner allows running multiple TestRunners
  15. /// and combining the results.
  16. /// </summary>
  17. public abstract class AggregatingTestRunner : MarshalByRefObject, TestRunner, EventListener
  18. {
  19. private Logger log;
  20. private Logger Log
  21. {
  22. get
  23. {
  24. if (log == null)
  25. log = InternalTrace.GetLogger(this.GetType());
  26. return log;
  27. }
  28. }
  29. static int AggregateTestID = 1000;
  30. #region Instance Variables
  31. /// <summary>
  32. /// Our runner ID
  33. /// </summary>
  34. protected int runnerID;
  35. /// <summary>
  36. /// The downstream TestRunners
  37. /// </summary>
  38. protected ArrayList runners;
  39. /// <summary>
  40. /// Indicates whether we should run test assemblies in parallel
  41. /// </summary>
  42. private bool runInParallel;
  43. /// <summary>
  44. /// The loaded test suite
  45. /// </summary>
  46. protected TestNode aggregateTest;
  47. /// <summary>
  48. /// The result of the last run
  49. /// </summary>
  50. private TestResult testResult;
  51. /// <summary>
  52. /// The event listener for the currently running test
  53. /// </summary>
  54. protected EventListener listener;
  55. protected TestName testName;
  56. #endregion
  57. #region Constructors
  58. public AggregatingTestRunner() : this( 0 ) { }
  59. public AggregatingTestRunner( int runnerID )
  60. {
  61. this.runnerID = runnerID;
  62. this.testName = new TestName();
  63. testName.TestID = new TestID( AggregateTestID );
  64. testName.RunnerID = this.runnerID;
  65. testName.FullName = testName.Name = "Not Loaded";
  66. }
  67. #endregion
  68. #region Properties
  69. public virtual int ID
  70. {
  71. get { return runnerID; }
  72. }
  73. public virtual bool Running
  74. {
  75. get
  76. {
  77. foreach( TestRunner runner in runners )
  78. if ( runner.Running )
  79. return true;
  80. return false;
  81. }
  82. }
  83. public virtual IList AssemblyInfo
  84. {
  85. get
  86. {
  87. ArrayList info = new ArrayList();
  88. foreach( TestRunner runner in runners )
  89. info.AddRange( runner.AssemblyInfo );
  90. return info;
  91. }
  92. }
  93. public virtual ITest Test
  94. {
  95. get
  96. {
  97. if ( aggregateTest == null && runners != null )
  98. {
  99. // Count non-null tests, in case we specified a fixture
  100. int count = 0;
  101. foreach( TestRunner runner in runners )
  102. if ( runner.Test != null )
  103. ++count;
  104. // Copy non-null tests to an array
  105. int index = 0;
  106. ITest[] tests = new ITest[count];
  107. foreach( TestRunner runner in runners )
  108. if ( runner.Test != null )
  109. tests[index++] = runner.Test;
  110. // Return master node containing all the tests
  111. aggregateTest = new TestNode( testName, tests );
  112. }
  113. return aggregateTest;
  114. }
  115. }
  116. public virtual TestResult TestResult
  117. {
  118. get { return testResult; }
  119. }
  120. #endregion
  121. #region Load and Unload Methods
  122. public bool Load(TestPackage package)
  123. {
  124. Log.Info("Loading " + package.Name);
  125. this.testName.FullName = this.testName.Name = package.FullName;
  126. runners = new ArrayList();
  127. int nfound = 0;
  128. int index = 0;
  129. string targetAssemblyName = null;
  130. if (package.TestName != null && package.Assemblies.Contains(package.TestName))
  131. {
  132. targetAssemblyName = package.TestName;
  133. package.TestName = null;
  134. }
  135. // NOTE: This is experimental. A normally created test package
  136. // will never have this setting.
  137. if (package.Settings.Contains("RunInParallel"))
  138. {
  139. this.runInParallel = true;
  140. package.Settings.Remove("RunInParallel");
  141. }
  142. //string basePath = package.BasePath;
  143. //if (basePath == null)
  144. // basePath = Path.GetDirectoryName(package.FullName);
  145. //string configFile = package.ConfigurationFile;
  146. //if (configFile == null && package.Name != null && !package.IsSingleAssembly)
  147. // configFile = Path.ChangeExtension(package.Name, ".config");
  148. foreach (string assembly in package.Assemblies)
  149. {
  150. if (targetAssemblyName == null || targetAssemblyName == assembly)
  151. {
  152. TestRunner runner = CreateRunner(this.runnerID * 100 + index + 1);
  153. TestPackage p = new TestPackage(assembly);
  154. p.AutoBinPath = package.AutoBinPath;
  155. p.ConfigurationFile = package.ConfigurationFile;
  156. p.BasePath = package.BasePath;
  157. p.PrivateBinPath = package.PrivateBinPath;
  158. p.TestName = package.TestName;
  159. foreach (object key in package.Settings.Keys)
  160. p.Settings[key] = package.Settings[key];
  161. if (package.TestName == null)
  162. {
  163. runners.Add(runner);
  164. if (runner.Load(p))
  165. nfound++;
  166. }
  167. else if (runner.Load(p))
  168. {
  169. runners.Add(runner);
  170. nfound++;
  171. }
  172. }
  173. }
  174. Log.Info("Load complete");
  175. if (package.TestName == null && targetAssemblyName == null)
  176. return nfound == package.Assemblies.Count;
  177. else
  178. return nfound > 0;
  179. }
  180. protected abstract TestRunner CreateRunner(int runnerID);
  181. public virtual void Unload()
  182. {
  183. if (aggregateTest != null)
  184. {
  185. Log.Info("Unloading " + Path.GetFileName(aggregateTest.TestName.Name));
  186. foreach (TestRunner runner in runners)
  187. runner.Unload();
  188. aggregateTest = null;
  189. Log.Info("Unload complete");
  190. }
  191. }
  192. #endregion
  193. #region CountTestCases
  194. public virtual int CountTestCases( ITestFilter filter )
  195. {
  196. int count = 0;
  197. foreach( TestRunner runner in runners )
  198. count += runner.CountTestCases( filter );
  199. return count;
  200. }
  201. #endregion
  202. #region Methods for Running Tests
  203. public virtual TestResult Run( EventListener listener )
  204. {
  205. return Run( listener, TestFilter.Empty );
  206. }
  207. // All forms of Run and BeginRun eventually come here
  208. public virtual TestResult Run(EventListener listener, ITestFilter filter )
  209. {
  210. Log.Info("Run - EventListener={0}", listener.GetType().Name);
  211. // Save active listener for derived classes
  212. this.listener = listener;
  213. ITest[] tests = new ITest[runners.Count];
  214. for( int index = 0; index < runners.Count; index++ )
  215. tests[index] = ((TestRunner)runners[index]).Test;
  216. string name = this.testName.Name;
  217. int count = this.CountTestCases(filter);
  218. Log.Info("Signalling RunStarted({0},{1})", name, count);
  219. this.listener.RunStarted(name, count);
  220. long startTime = DateTime.Now.Ticks;
  221. TestResult result = new TestResult(new TestInfo(testName, tests));
  222. if (this.runInParallel)
  223. {
  224. foreach (TestRunner runner in runners)
  225. if (filter.Pass(runner.Test))
  226. runner.BeginRun(this, filter);
  227. result = this.EndRun();
  228. }
  229. else
  230. {
  231. foreach (TestRunner runner in runners)
  232. if (filter.Pass(runner.Test))
  233. result.AddResult(runner.Run(this, filter));
  234. }
  235. long stopTime = DateTime.Now.Ticks;
  236. double time = ((double)(stopTime - startTime)) / (double)TimeSpan.TicksPerSecond;
  237. result.Time = time;
  238. this.listener.RunFinished( result );
  239. this.testResult = result;
  240. return result;
  241. }
  242. public virtual void BeginRun( EventListener listener )
  243. {
  244. BeginRun( listener, TestFilter.Empty );
  245. }
  246. public virtual void BeginRun( EventListener listener, ITestFilter filter )
  247. {
  248. // Save active listener for derived classes
  249. this.listener = listener;
  250. Log.Info("BeginRun");
  251. // ThreadedTestRunner will call our Run method on a separate thread
  252. ThreadedTestRunner threadedRunner = new ThreadedTestRunner(this);
  253. threadedRunner.BeginRun(listener, filter);
  254. }
  255. public virtual TestResult EndRun()
  256. {
  257. Log.Info("EndRun");
  258. TestResult suiteResult = new TestResult(Test as TestInfo);
  259. foreach( TestRunner runner in runners )
  260. suiteResult.Results.Add( runner.EndRun() );
  261. return suiteResult;
  262. }
  263. public virtual void CancelRun()
  264. {
  265. foreach( TestRunner runner in runners )
  266. runner.CancelRun();
  267. }
  268. public virtual void Wait()
  269. {
  270. foreach( TestRunner runner in runners )
  271. runner.Wait();
  272. }
  273. #endregion
  274. #region EventListener Members
  275. public void TestStarted(TestName testName)
  276. {
  277. this.listener.TestStarted( testName );
  278. }
  279. public void RunStarted(string name, int testCount)
  280. {
  281. // TODO: We may want to count how many runs are started
  282. // Ignore - we provide our own
  283. }
  284. public void RunFinished(Exception exception)
  285. {
  286. // Ignore - we provide our own
  287. }
  288. void NUnit.Core.EventListener.RunFinished(TestResult result)
  289. {
  290. if (this.runInParallel)
  291. {
  292. foreach (TestRunner runner in runners)
  293. if (runner.Running)
  294. return;
  295. this.testResult = new TestResult(this.aggregateTest);
  296. foreach (TestRunner runner in runners)
  297. this.testResult.AddResult(runner.TestResult);
  298. listener.RunFinished(this.TestResult);
  299. }
  300. }
  301. public void SuiteFinished(TestResult result)
  302. {
  303. this.listener.SuiteFinished( result );
  304. }
  305. public void TestFinished(TestResult result)
  306. {
  307. this.listener.TestFinished( result );
  308. }
  309. public void UnhandledException(Exception exception)
  310. {
  311. this.listener.UnhandledException( exception );
  312. }
  313. public void TestOutput(TestOutput testOutput)
  314. {
  315. this.listener.TestOutput( testOutput );
  316. }
  317. public void SuiteStarted(TestName suiteName)
  318. {
  319. this.listener.SuiteStarted( suiteName );
  320. }
  321. #endregion
  322. #region InitializeLifetimeService Override
  323. public override object InitializeLifetimeService()
  324. {
  325. return null;
  326. }
  327. #endregion
  328. #region IDisposable Members
  329. public void Dispose()
  330. {
  331. foreach (TestRunner runner in runners)
  332. if (runner != null)
  333. runner.Dispose();
  334. }
  335. #endregion
  336. }
  337. #endregion
  338. #region MultipleTestDomainRunner
  339. /// <summary>
  340. /// Summary description for MultipleTestDomainRunner.
  341. /// </summary>
  342. public class MultipleTestDomainRunner : AggregatingTestRunner
  343. {
  344. #region Constructors
  345. public MultipleTestDomainRunner() : base(0) { }
  346. public MultipleTestDomainRunner(int runnerID) : base(runnerID) { }
  347. #endregion
  348. #region CreateRunner
  349. protected override TestRunner CreateRunner(int runnerID)
  350. {
  351. return new TestDomain(runnerID);
  352. }
  353. #endregion
  354. }
  355. #endregion
  356. #region MultipleTestProcessRunner
  357. public class MultipleTestProcessRunner : AggregatingTestRunner
  358. {
  359. #region Constructors
  360. public MultipleTestProcessRunner() : base(0) { }
  361. public MultipleTestProcessRunner(int runnerID) : base(runnerID) { }
  362. #endregion
  363. #region CreateRunner
  364. protected override TestRunner CreateRunner(int runnerID)
  365. {
  366. return new ProcessRunner(runnerID);
  367. }
  368. #endregion
  369. }
  370. #endregion
  371. }