PageRenderTime 92ms CodeModel.GetById 34ms app.highlight 44ms RepoModel.GetById 1ms app.codeStats 1ms

/src/NUnit/util/AggregatingTestRunner.cs

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