DataObjectAttribute.cs: Implemented
[mono.git] / mcs / nunit20 / core / RemoteTestRunner.cs
1 #region Copyright (c) 2002-2003, James W. Newkirk, Michael C. Two, Alexei A. Vorontsov, Charlie Poole, Philip A. Craig
2 /************************************************************************************
3 '
4 ' Copyright © 2002-2003 James W. Newkirk, Michael C. Two, Alexei A. Vorontsov, Charlie Poole
5 ' Copyright © 2000-2003 Philip A. Craig
6 '
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 
9 ' software.
10
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:
14 '
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.
18 '
19 ' Portions Copyright © 2003 James W. Newkirk, Michael C. Two, Alexei A. Vorontsov, Charlie Poole
20 ' or Copyright © 2000-2003 Philip A. Craig
21 '
22 ' 2. Altered source versions must be plainly marked as such, and must not be 
23 ' misrepresented as being the original software.
24 '
25 ' 3. This notice may not be removed or altered from any source distribution.
26 '
27 '***********************************************************************************/
28 #endregion
29
30 namespace NUnit.Core
31 {
32         using System;
33         using System.IO;
34         using System.Collections;
35         using System.Reflection;
36         using System.Threading;
37         using System.Runtime.Remoting;
38
39         /// <summary>
40         /// Summary description for RemoteTestRunner.
41         /// </summary>
42         /// 
43         [Serializable]
44         public class RemoteTestRunner : LongLivingMarshalByRefObject, TestRunner, EventListener
45         {
46                 #region Instance variables
47
48                 /// <summary>
49                 /// The loaded test suite
50                 /// </summary>
51                 private TestSuite suite;
52
53                 /// <summary>
54                 /// TestRunner thread used for asynchronous running
55                 /// </summary>
56                 private TestRunnerThread runningThread;
57
58                 /// <summary>
59                 /// Our writer for standard output
60                 /// </summary>
61                 private TextWriter outText;
62
63                 /// <summary>
64                 /// Our writer for error output
65                 /// </summary>
66                 private TextWriter errorText;
67
68                 /// <summary>
69                 /// Buffered standard output writer created for each test run
70                 /// </summary>
71                 private BufferedStringTextWriter outBuffer;
72
73                 /// <summary>
74                 /// Buffered error writer created for each test run
75                 /// </summary>
76                 private BufferedStringTextWriter errorBuffer;
77
78                 /// <summary>
79                 /// Console standard output to restore after a run
80                 /// </summary>
81                 private TextWriter saveOut;
82
83                 /// <summary>
84                 /// Console error output to restore after a run
85                 /// </summary>
86                 private TextWriter saveError;
87
88                 /// <summary>
89                 /// Saved current directory to restore after a run
90                 /// </summary>
91                 private string currentDirectory;
92
93                 /// <summary>
94                 /// Saved paths of the assemblies we loaded - used to set 
95                 /// current directory when we are running the tests.
96                 /// </summary>
97                 private string[] assemblies;
98
99                 /// <summary>
100                 /// Dispatcher used to put out runner's test events
101                 /// </summary>
102                 private TestEventDispatcher events = new TestEventDispatcher();
103
104                 private EventListener listener; // Temp
105
106                 private Version frameworkVersion;
107
108                 private IFilter filter;
109
110                 private bool displayTestLabels;
111
112                 /// <summary>
113                 /// Results from the last test run
114                 /// </summary>
115                 private TestResult[] results;
116
117                 #endregion
118
119                 #region Constructors
120
121                 /// <summary>
122                 /// Construct with stdOut and stdErr writers
123                 /// </summary>
124                 public RemoteTestRunner( TextWriter outText, TextWriter errorText )
125                 {
126                         this.outText = outText;
127                         this.errorText = errorText;
128                 }
129
130                 /// <summary>
131                 /// Default constructor uses Null writers.
132                 /// </summary>
133                 public RemoteTestRunner() : this( TextWriter.Null, TextWriter.Null ) { }
134
135                 #endregion
136
137                 #region Properties
138
139                 /// <summary>
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.
143                 /// </summary>
144                 public TextWriter Out
145                 {
146                         get { return outText; }
147                         set { outText = value; }
148                 }
149
150                 /// <summary>
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.
154                 /// </summary>
155                 public TextWriter Error
156                 {
157                         get { return errorText; }
158                         set { errorText = value; }
159                 }
160
161                 /// <summary>
162                 /// Interface to the events sourced by the runner
163                 /// </summary>
164                 public ITestEvents Events
165                 {
166                         get { return events; }
167                 }
168
169                 public Version FrameworkVersion
170                 {
171                         get { return frameworkVersion; }
172                 }
173
174                 public bool DisplayTestLabels
175                 {
176                         get { return displayTestLabels; }
177                         set { displayTestLabels = value; }
178                 }
179
180                 /// <summary>
181                 /// Results from the last test run
182                 /// </summary>
183                 public TestResult[] Results
184                 {
185                         get { return results; }
186                 }
187
188                 /// <summary>
189                 /// First (or only) result from the last test run
190                 /// </summary>
191                 public TestResult Result
192                 {
193                         get { return results == null ? null : results[0]; }
194                 }
195
196                 #endregion
197
198                 #region Methods for Loading Tests
199
200                 /// <summary>
201                 /// Load an assembly
202                 /// </summary>
203                 /// <param name="assemblyName"></param>
204                 public Test Load( string assemblyName )
205                 {
206                         this.assemblies = new string[] { assemblyName };
207                         TestSuiteBuilder builder = new TestSuiteBuilder();
208                         suite = builder.Build( assemblyName );
209                         frameworkVersion = builder.FrameworkVersion;
210                         return suite;
211                 }
212
213                 /// <summary>
214                 /// Load a particular test in an assembly
215                 /// </summary>
216                 public Test Load( string assemblyName, string testName )
217                 {
218                         this.assemblies = new string[] { assemblyName };
219                         TestSuiteBuilder builder = new TestSuiteBuilder();
220                         suite = builder.Build( assemblyName, testName );
221                         frameworkVersion = builder.FrameworkVersion;
222                         return suite;
223                 }
224
225                 /// <summary>
226                 /// Load multiple assemblies
227                 /// </summary>
228                 public Test Load( string projectName, string[] assemblies )
229                 {
230                         this.assemblies = (string[])assemblies.Clone();
231                         TestSuiteBuilder builder = new TestSuiteBuilder();
232                         suite = builder.Build( projectName, assemblies );
233                         frameworkVersion = builder.FrameworkVersion;
234                         return suite;
235                 }
236
237                 public Test Load( string projectName, string[] assemblies, string testName )
238                 {
239                         this.assemblies = (string[])assemblies.Clone();
240                         TestSuiteBuilder builder = new TestSuiteBuilder();
241                         suite = builder.Build( assemblies, testName );
242                         frameworkVersion = builder.FrameworkVersion;
243                         return suite;
244                 }
245
246                 public void Unload()
247                 {
248                         suite = null; // All for now
249                         frameworkVersion = null;
250                 }
251
252                 #endregion
253
254                 #region Methods for Counting TestCases
255
256                 public int CountTestCases()
257                 {
258                         return suite.CountTestCases();
259                 }
260
261                 public int CountTestCases( string testName )
262                 {
263                         Test test = FindTest( suite, testName );
264                         return test == null ? 0 : test.CountTestCases();
265                 }
266
267                 public int CountTestCases(string[] testNames ) 
268                 {
269                         int count = 0;
270                         foreach( string testName in testNames)
271                                 count += CountTestCases( testName );
272
273                         return count;
274                 }
275
276                 public ICollection GetCategories()
277                 {
278                         return CategoryManager.Categories;
279                 }
280
281                 #endregion
282
283                 #region Methods for Running Tests
284
285                 public void SetFilter( IFilter filter )
286                 {
287                         this.filter = filter;
288                 }
289
290                 public TestResult Run( EventListener listener )
291                 {
292                         return Run( listener, suite );
293                 }
294
295                 public TestResult Run(NUnit.Core.EventListener listener, string testName )
296                 {
297                         if ( testName == null || testName.Length == 0 )
298                                 return Run( listener, suite );
299                         else
300                                 return Run( listener, FindTest( suite, testName ) );
301                 }
302
303                 public TestResult[] Run(NUnit.Core.EventListener listener, string[] testNames)
304                 {
305                         if ( testNames == null || testNames.Length == 0 )
306                                 return new TestResult[] { Run( listener, suite ) };
307                         else
308                                 return Run( listener, FindTests( suite, testNames ) );
309                 }
310
311                 public void RunTest(NUnit.Core.EventListener listener )
312                 {
313                         runningThread = new TestRunnerThread( this );
314                         runningThread.Run( listener );
315                 }
316                 
317                 public void RunTest(NUnit.Core.EventListener listener, string testName )
318                 {
319                         runningThread = new TestRunnerThread( this );
320                         runningThread.Run( listener, testName );
321                 }
322
323                 public void RunTest(NUnit.Core.EventListener listener, string[] testNames)
324                 {
325                         runningThread = new TestRunnerThread( this );
326                         runningThread.Run( listener, testNames );
327                 }
328
329                 public void CancelRun()
330                 {
331                         if ( runningThread != null )
332                                 runningThread.Cancel();
333
334                         CleanUpAfterTestRun();
335                 }
336
337                 public void Wait()
338                 {
339                         if ( runningThread != null )
340                                 runningThread.Wait();
341                 }
342
343                 #endregion
344
345                 #region Helper Routines
346
347                 /// <summary>
348                 /// Private method to run a single test
349                 /// </summary>
350                 private TestResult Run( EventListener listener, Test test )
351                 {
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
357                         return results[0];
358                 }
359
360                 /// <summary>
361                 /// Private method to run a set of tests. This routine is the workhorse
362                 /// that is called anytime tests are run.
363                 /// </summary>
364                 private TestResult[] Run( EventListener listener, Test[] tests )
365                 {
366                         // Create buffered writers for efficiency
367                         outBuffer = new BufferedStringTextWriter( outText );
368                         errorBuffer = new BufferedStringTextWriter( errorText );
369
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;
376 #if !TARGET_JVM
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 ); 
381 #endif
382                         // Save the current directory so we can run each test in
383                         // the same directory as its assembly
384                         currentDirectory = Environment.CurrentDirectory;
385                         
386                         try
387                         {
388                                 // Create an array for the resuls
389                                 results = new TestResult[ tests.Length ];
390
391                                 // Signal that we are starting the run
392                                 this.listener = listener;
393                                 listener.RunStarted( tests );
394                                 
395                                 // TODO: Get rid of count
396                                 int count = 0;
397                                 foreach( Test test in tests )
398                                         count += filter == null ? test.CountTestCases() : test.CountTestCases( filter );
399
400                                 events.FireRunStarting( tests, count );
401                                 
402                                 // Run each test, saving the results
403                                 int index = 0;
404                                 foreach( Test test in tests )
405                                 {
406                                         string assemblyDirectory = Path.GetDirectoryName( this.assemblies[test.AssemblyKey] );
407
408                                         if ( assemblyDirectory != null && assemblyDirectory != string.Empty )
409                                                 Environment.CurrentDirectory = assemblyDirectory;
410
411                                         results[index++] = test.Run( this, filter );
412                                 }
413
414                                 // Signal that we are done
415                                 listener.RunFinished( results );
416                                 events.FireRunFinished( results );
417
418                                 // Return result array
419                                 return results;
420                         }
421                         catch( Exception exception )
422                         {
423                                 // Signal that we finished with an exception
424                                 listener.RunFinished( exception );
425                                 events.FireRunFinished( exception );
426                                 // Rethrow - should we do this?
427                                 throw;
428                         }
429                         finally
430                         {
431                                 CleanUpAfterTestRun();
432                         }
433                 }
434
435                 private Test FindTest(Test test, string fullName)
436                 {
437                         if(test.UniqueName.Equals(fullName)) return test;
438                         if(test.FullName.Equals(fullName)) return test;
439                         
440                         Test result = null;
441                         if(test is TestSuite)
442                         {
443                                 TestSuite suite = (TestSuite)test;
444                                 foreach(Test testCase in suite.Tests)
445                                 {
446                                         result = FindTest(testCase, fullName);
447                                         if(result != null) break;
448                                 }
449                         }
450
451                         return result;
452                 }
453
454                 private Test[] FindTests( Test test, string[] names )
455                 {
456                         Test[] tests = new Test[ names.Length ];
457
458                         int index = 0;
459                         foreach( string name in names )
460                                 tests[index++] = FindTest( test, name );
461
462                         return tests;
463                 }
464
465                 private void CleanUpAfterTestRun()
466                 {
467                         // Restore the directory we saved
468                         if ( currentDirectory != null )
469                         {
470                                 Environment.CurrentDirectory = currentDirectory;
471                                 currentDirectory = null;
472                         }
473
474                         // Close our output buffers
475                         if ( outBuffer != null )
476                         {
477                                 outBuffer.Close();
478                                 outBuffer = null;
479                         }
480
481                         if ( errorBuffer != null )
482                         {
483                                 errorBuffer.Close();
484                                 errorBuffer = null;
485                         }
486
487                         // Restore previous console values
488                         if ( saveOut != null )
489                         {
490                                 Console.SetOut( saveOut );
491                                 saveOut = null;
492                         }
493
494                         if ( saveError != null )
495                         {
496                                 Console.SetError( saveError );
497                                 saveError = null;
498                         }
499                 }
500
501                 #endregion
502
503                 #region EventListener Members
504
505                 public void RunStarted(Test[] tests)
506                 {
507                         // TODO:  Remove
508                 }
509
510                 void NUnit.Core.EventListener.RunFinished(TestResult[] results)
511                 {
512                         // TODO:  Remove
513                         outText.Close();
514                 }
515
516                 void NUnit.Core.EventListener.RunFinished(Exception exception)
517                 {
518                         // TODO:  Remove
519                         outText.Close();
520                 }
521
522                 public void TestStarted(TestCase testCase)
523                 {
524                         if ( displayTestLabels )
525                                 outText.WriteLine("***** {0}", testCase.FullName );
526                         
527                         this.listener.TestStarted( testCase );
528                         events.FireTestStarting( testCase );
529                 }
530
531                 void NUnit.Core.EventListener.TestFinished(TestCaseResult result)
532                 {
533                         listener.TestFinished( result );
534                         events.FireTestFinished( result );
535                 }
536
537                 public void SuiteStarted(TestSuite suite)
538                 {
539                         listener.SuiteStarted( suite );
540                         events.FireSuiteStarting( suite );
541                 }
542
543                 void NUnit.Core.EventListener.SuiteFinished(TestSuiteResult result)
544                 {
545                         listener.SuiteFinished( result );
546                         events.FireSuiteFinished( result );
547                 }
548
549                 public void UnhandledException(Exception exception)
550                 {
551                         listener.UnhandledException( exception );
552                         events.FireTestException( exception );
553                 }
554
555                 #endregion
556         }
557 }
558