1 #region Copyright (c) 2002-2003, James W. Newkirk, Michael C. Two, Alexei A. Vorontsov, Charlie Poole, Philip A. Craig
2 /************************************************************************************
4 ' Copyright © 2002-2003 James W. Newkirk, Michael C. Two, Alexei A. Vorontsov, Charlie Poole
5 ' Copyright © 2000-2003 Philip A. Craig
7 ' This software is provided 'as-is', without any express or implied warranty. In no
8 ' event will the authors be held liable for any damages arising from the use of this
11 ' Permission is granted to anyone to use this software for any purpose, including
12 ' commercial applications, and to alter it and redistribute it freely, subject to the
13 ' following restrictions:
15 ' 1. The origin of this software must not be misrepresented; you must not claim that
16 ' you wrote the original software. If you use this software in a product, an
17 ' acknowledgment (see the following) in the product documentation is required.
19 ' Portions Copyright © 2003 James W. Newkirk, Michael C. Two, Alexei A. Vorontsov, Charlie Poole
20 ' or Copyright © 2000-2003 Philip A. Craig
22 ' 2. Altered source versions must be plainly marked as such, and must not be
23 ' misrepresented as being the original software.
25 ' 3. This notice may not be removed or altered from any source distribution.
27 '***********************************************************************************/
34 using System.Collections;
35 using System.Reflection;
36 using System.Threading;
37 using System.Runtime.Remoting;
40 /// Summary description for RemoteTestRunner.
44 public class RemoteTestRunner : LongLivingMarshalByRefObject, TestRunner, EventListener
46 #region Instance variables
49 /// The loaded test suite
51 private TestSuite suite;
54 /// TestRunner thread used for asynchronous running
56 private TestRunnerThread runningThread;
59 /// Our writer for standard output
61 private TextWriter outText;
64 /// Our writer for error output
66 private TextWriter errorText;
69 /// Buffered standard output writer created for each test run
71 private BufferedStringTextWriter outBuffer;
74 /// Buffered error writer created for each test run
76 private BufferedStringTextWriter errorBuffer;
79 /// Console standard output to restore after a run
81 private TextWriter saveOut;
84 /// Console error output to restore after a run
86 private TextWriter saveError;
89 /// Saved current directory to restore after a run
91 private string currentDirectory;
94 /// Saved paths of the assemblies we loaded - used to set
95 /// current directory when we are running the tests.
97 private string[] assemblies;
100 /// Dispatcher used to put out runner's test events
102 private TestEventDispatcher events = new TestEventDispatcher();
104 private EventListener listener; // Temp
106 private Version frameworkVersion;
108 private IFilter filter;
110 private bool displayTestLabels;
113 /// Results from the last test run
115 private TestResult[] results;
122 /// Construct with stdOut and stdErr writers
124 public RemoteTestRunner( TextWriter outText, TextWriter errorText )
126 this.outText = outText;
127 this.errorText = errorText;
131 /// Default constructor uses Null writers.
133 public RemoteTestRunner() : this( TextWriter.Null, TextWriter.Null ) { }
140 /// Writer for standard output - this is a public property
141 /// so that we can set it when creating an instance
142 /// in another AppDomain.
144 public TextWriter Out
146 get { return outText; }
147 set { outText = value; }
151 /// Writer for error output - this is a public property
152 /// so that we can set it when creating an instance
153 /// in another AppDomain.
155 public TextWriter Error
157 get { return errorText; }
158 set { errorText = value; }
162 /// Interface to the events sourced by the runner
164 public ITestEvents Events
166 get { return events; }
169 public Version FrameworkVersion
171 get { return frameworkVersion; }
174 public bool DisplayTestLabels
176 get { return displayTestLabels; }
177 set { displayTestLabels = value; }
181 /// Results from the last test run
183 public TestResult[] Results
185 get { return results; }
189 /// First (or only) result from the last test run
191 public TestResult Result
193 get { return results == null ? null : results[0]; }
198 #region Methods for Loading Tests
203 /// <param name="assemblyName"></param>
204 public Test Load( string assemblyName )
206 this.assemblies = new string[] { assemblyName };
207 TestSuiteBuilder builder = new TestSuiteBuilder();
208 suite = builder.Build( assemblyName );
209 frameworkVersion = builder.FrameworkVersion;
214 /// Load a particular test in an assembly
216 public Test Load( string assemblyName, string testName )
218 this.assemblies = new string[] { assemblyName };
219 TestSuiteBuilder builder = new TestSuiteBuilder();
220 suite = builder.Build( assemblyName, testName );
221 frameworkVersion = builder.FrameworkVersion;
226 /// Load multiple assemblies
228 public Test Load( string projectName, string[] assemblies )
230 this.assemblies = (string[])assemblies.Clone();
231 TestSuiteBuilder builder = new TestSuiteBuilder();
232 suite = builder.Build( projectName, assemblies );
233 frameworkVersion = builder.FrameworkVersion;
237 public Test Load( string projectName, string[] assemblies, string testName )
239 this.assemblies = (string[])assemblies.Clone();
240 TestSuiteBuilder builder = new TestSuiteBuilder();
241 suite = builder.Build( assemblies, testName );
242 frameworkVersion = builder.FrameworkVersion;
248 suite = null; // All for now
249 frameworkVersion = null;
254 #region Methods for Counting TestCases
256 public int CountTestCases()
258 return suite.CountTestCases();
261 public int CountTestCases( string testName )
263 Test test = FindTest( suite, testName );
264 return test == null ? 0 : test.CountTestCases();
267 public int CountTestCases(string[] testNames )
270 foreach( string testName in testNames)
271 count += CountTestCases( testName );
276 public ICollection GetCategories()
278 return CategoryManager.Categories;
283 #region Methods for Running Tests
285 public void SetFilter( IFilter filter )
287 this.filter = filter;
290 public TestResult Run( EventListener listener )
292 return Run( listener, suite );
295 public TestResult Run(NUnit.Core.EventListener listener, string testName )
297 if ( testName == null || testName.Length == 0 )
298 return Run( listener, suite );
300 return Run( listener, FindTest( suite, testName ) );
303 public TestResult[] Run(NUnit.Core.EventListener listener, string[] testNames)
305 if ( testNames == null || testNames.Length == 0 )
306 return new TestResult[] { Run( listener, suite ) };
308 return Run( listener, FindTests( suite, testNames ) );
311 public void RunTest(NUnit.Core.EventListener listener )
313 runningThread = new TestRunnerThread( this );
314 runningThread.Run( listener );
317 public void RunTest(NUnit.Core.EventListener listener, string testName )
319 runningThread = new TestRunnerThread( this );
320 runningThread.Run( listener, testName );
323 public void RunTest(NUnit.Core.EventListener listener, string[] testNames)
325 runningThread = new TestRunnerThread( this );
326 runningThread.Run( listener, testNames );
329 public void CancelRun()
331 if ( runningThread != null )
332 runningThread.Cancel();
334 CleanUpAfterTestRun();
339 if ( runningThread != null )
340 runningThread.Wait();
345 #region Helper Routines
348 /// Private method to run a single test
350 private TestResult Run( EventListener listener, Test test )
352 // Create array with the one test
353 Test[] tests = new Test[] { test };
354 // Call our workhorse method
355 results = Run( listener, tests );
356 // Return the first result we got
361 /// Private method to run a set of tests. This routine is the workhorse
362 /// that is called anytime tests are run.
364 private TestResult[] Run( EventListener listener, Test[] tests )
366 // Create buffered writers for efficiency
367 outBuffer = new BufferedStringTextWriter( outText );
368 errorBuffer = new BufferedStringTextWriter( errorText );
370 // Save previous state of Console. This is needed because Console.Out and
371 // Console.Error are static. In the case where the test itself calls this
372 // method, we can lose output if we don't save and restore their values.
373 // This is exactly what happens when we are testing NUnit itself.
374 saveOut = Console.Out;
375 saveError = Console.Error;
377 // Set Console to go to our buffers. Note that any changes made by
378 // the user in the test code or the code it calls will defeat this.
379 Console.SetOut( outBuffer );
380 Console.SetError( errorBuffer );
382 // Save the current directory so we can run each test in
383 // the same directory as its assembly
384 currentDirectory = Environment.CurrentDirectory;
388 // Create an array for the resuls
389 results = new TestResult[ tests.Length ];
391 // Signal that we are starting the run
392 this.listener = listener;
393 listener.RunStarted( tests );
395 // TODO: Get rid of count
397 foreach( Test test in tests )
398 count += filter == null ? test.CountTestCases() : test.CountTestCases( filter );
400 events.FireRunStarting( tests, count );
402 // Run each test, saving the results
404 foreach( Test test in tests )
406 string assemblyDirectory = Path.GetDirectoryName( this.assemblies[test.AssemblyKey] );
408 if ( assemblyDirectory != null && assemblyDirectory != string.Empty )
409 Environment.CurrentDirectory = assemblyDirectory;
411 results[index++] = test.Run( this, filter );
414 // Signal that we are done
415 listener.RunFinished( results );
416 events.FireRunFinished( results );
418 // Return result array
421 catch( Exception exception )
423 // Signal that we finished with an exception
424 listener.RunFinished( exception );
425 events.FireRunFinished( exception );
426 // Rethrow - should we do this?
431 CleanUpAfterTestRun();
435 private Test FindTest(Test test, string fullName)
437 if(test.UniqueName.Equals(fullName)) return test;
438 if(test.FullName.Equals(fullName)) return test;
441 if(test is TestSuite)
443 TestSuite suite = (TestSuite)test;
444 foreach(Test testCase in suite.Tests)
446 result = FindTest(testCase, fullName);
447 if(result != null) break;
454 private Test[] FindTests( Test test, string[] names )
456 Test[] tests = new Test[ names.Length ];
459 foreach( string name in names )
460 tests[index++] = FindTest( test, name );
465 private void CleanUpAfterTestRun()
467 // Restore the directory we saved
468 if ( currentDirectory != null )
470 Environment.CurrentDirectory = currentDirectory;
471 currentDirectory = null;
474 // Close our output buffers
475 if ( outBuffer != null )
481 if ( errorBuffer != null )
487 // Restore previous console values
488 if ( saveOut != null )
490 Console.SetOut( saveOut );
494 if ( saveError != null )
496 Console.SetError( saveError );
503 #region EventListener Members
505 public void RunStarted(Test[] tests)
510 void NUnit.Core.EventListener.RunFinished(TestResult[] results)
516 void NUnit.Core.EventListener.RunFinished(Exception exception)
522 public void TestStarted(TestCase testCase)
524 if ( displayTestLabels )
525 outText.WriteLine("***** {0}", testCase.FullName );
527 this.listener.TestStarted( testCase );
528 events.FireTestStarting( testCase );
531 void NUnit.Core.EventListener.TestFinished(TestCaseResult result)
533 listener.TestFinished( result );
534 events.FireTestFinished( result );
537 public void SuiteStarted(TestSuite suite)
539 listener.SuiteStarted( suite );
540 events.FireSuiteStarting( suite );
543 void NUnit.Core.EventListener.SuiteFinished(TestSuiteResult result)
545 listener.SuiteFinished( result );
546 events.FireSuiteFinished( result );
549 public void UnhandledException(Exception exception)
551 listener.UnhandledException( exception );
552 events.FireTestException( exception );