2009-06-12 Bill Holmes <billholmes54@gmail.com>
[mono.git] / mcs / nunit20 / nunit-console / ConsoleUi.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.Console
31 {
32         using System;
33         using System.Collections;
34         using System.Collections.Specialized;
35         using System.IO;
36         using System.Reflection;
37         using System.Xml;
38         using System.Xml.Xsl;
39         using System.Xml.XPath;
40         using System.Resources;
41         using System.Text;
42         using System.Text.RegularExpressions;
43         using System.Diagnostics;
44         using NUnit.Core;
45         using NUnit.Util;
46         
47
48         /// <summary>
49         /// Summary description for ConsoleUi.
50         /// </summary>
51         public class ConsoleUi
52         {
53 #if !TARGET_JVM
54                 [STAThread]
55 #endif
56                 public static int Main(string[] args)
57                 {
58                         ConsoleOptions options = new ConsoleOptions(args);
59                         if(!options.nologo)
60                                 WriteCopyright();
61
62                         if(options.help)
63                         {
64                                 options.Help();
65                                 return 0;
66                         }
67                         
68                         if(options.NoArgs) 
69                         {
70                                 Console.Error.WriteLine("fatal error: no inputs specified");
71                                 options.Help();
72                                 return 0;
73                         }
74                         
75                         if(!options.Validate())
76                         {
77                                 Console.Error.WriteLine("fatal error: invalid arguments");
78                                 options.Help();
79                                 return 2;
80                         }
81
82                         try
83                         {
84                                 ConsoleUi consoleUi = new ConsoleUi();
85                                 return consoleUi.Execute( options );
86                         }
87                         catch( FileNotFoundException ex )
88                         {
89                                 Console.WriteLine( ex.Message );
90                                 return 2;
91                         }
92                         catch( BadImageFormatException ex )
93                         {
94                                 Console.WriteLine( ex.Message );
95                                 return 2;
96                         }
97                         catch( Exception ex )
98                         {
99                                 Console.WriteLine( "Unhandled Exception:\n{0}", ex.ToString() );
100                                 return 2;
101                         }
102                         finally
103                         {
104                                 if(options.wait)
105                                 {
106                                         Console.Out.WriteLine("\nHit <enter> key to continue");
107                                         Console.ReadLine();
108                                 }
109                         }
110                 }
111
112                 private static XmlTextReader GetTransformReader(ConsoleOptions parser)
113                 {
114                         XmlTextReader reader = null;
115                         if(!parser.IsTransform)
116                         {
117                                 Assembly assembly = Assembly.GetAssembly(typeof(XmlResultVisitor));
118                                 ResourceManager resourceManager = new ResourceManager("NUnit.Util.Transform",assembly);
119                                 string xmlData = (string)resourceManager.GetObject("Summary.xslt");
120
121                                 reader = new XmlTextReader(new StringReader(xmlData));
122                         }
123                         else
124                         {
125                                 FileInfo xsltInfo = new FileInfo(parser.transform);
126                                 if(!xsltInfo.Exists)
127                                 {
128                                         Console.Error.WriteLine("Transform file: {0} does not exist", xsltInfo.FullName);
129                                         reader = null;
130                                 }
131                                 else
132                                 {
133                                         reader = new XmlTextReader(xsltInfo.FullName);
134                                 }
135                         }
136
137                         return reader;
138                 }
139
140                 private static void WriteCopyright()
141                 {
142                         Assembly executingAssembly = Assembly.GetExecutingAssembly();
143                         System.Version version = executingAssembly.GetName().Version;
144
145                         object[] objectAttrs = executingAssembly.GetCustomAttributes(typeof(AssemblyProductAttribute), false);
146                         AssemblyProductAttribute productAttr = (AssemblyProductAttribute)objectAttrs[0];
147
148                         objectAttrs = executingAssembly.GetCustomAttributes(typeof(AssemblyCopyrightAttribute), false);
149                         AssemblyCopyrightAttribute copyrightAttr = (AssemblyCopyrightAttribute)objectAttrs[0];
150
151                         Console.WriteLine(String.Format("{0} version {1}", productAttr.Product, version.ToString(3)));
152                         Console.WriteLine(copyrightAttr.Copyright);
153                         Console.WriteLine();
154
155                         string clrPlatform = Type.GetType("Mono.Runtime", false) == null ? ".NET" : "Mono";
156 #if !TARGET_JVM                 
157                         Console.WriteLine( string.Format("OS Version: {0}    {1} Version: {2}",
158                                 Environment.OSVersion, clrPlatform, Environment.Version ) );
159 #endif
160                         Console.WriteLine();
161                 }
162
163                 private static Test MakeTestFromCommandLine(TestDomain testDomain, ConsoleOptions parser)
164                 {
165                         NUnitProject project;
166
167                         if ( parser.IsTestProject )
168                         {
169                                 project = NUnitProject.LoadProject( (string)parser.Parameters[0] );
170                                 string configName = (string) parser.config;
171                                 if ( configName != null )
172                                         project.SetActiveConfig( configName );
173                         }
174                         else
175                                 project = NUnitProject.FromAssemblies( (string[])parser.Parameters.ToArray( typeof( string ) ) );
176
177                         return testDomain.Load( project, parser.fixture );
178                 }
179
180                 public ConsoleUi()
181                 {
182                 }
183
184                 public int Execute( ConsoleOptions options )
185                 {
186                         XmlTextReader transformReader = GetTransformReader(options);
187                         if(transformReader == null) return 3;
188
189                         ConsoleWriter outStream = options.isOut
190                                 ? new ConsoleWriter( new StreamWriter( options.output ) )
191                                 : new ConsoleWriter(Console.Out);
192
193                         ConsoleWriter errorStream = options.isErr
194                                 ? new ConsoleWriter( new StreamWriter( options.err ) )
195                                 : new ConsoleWriter(Console.Error);
196
197                         TestDomain testDomain = new TestDomain(outStream, errorStream);
198                         if ( options.noshadow  ) testDomain.ShadowCopyFiles = false;
199
200                         Test test = MakeTestFromCommandLine(testDomain, options);
201
202                         if(test == null)
203                         {
204                                 Console.Error.WriteLine("Unable to locate fixture {0}", options.fixture);
205                                 return 2;
206                         }
207
208                         Directory.SetCurrentDirectory(new FileInfo((string)options.Parameters[0]).DirectoryName);
209                 
210                         EventCollector collector = new EventCollector( options, outStream );
211
212                         string savedDirectory = Environment.CurrentDirectory;
213
214                         if (options.HasInclude)
215                         {
216                                 Console.WriteLine( "Included categories: " + options.include );
217                                 testDomain.SetFilter( new CategoryFilter( options.IncludedCategories ) );
218                         }
219                         else if ( options.HasExclude )
220                         {
221                                 Console.WriteLine( "Excluded categories: " + options.exclude );
222                                 testDomain.SetFilter( new CategoryFilter( options.ExcludedCategories, true ) );
223                         }
224
225                         TestResult result = null;
226                         if ( options.thread )
227                         {
228                                 testDomain.RunTest( collector );
229                                 testDomain.Wait();
230                                 result = testDomain.Result;
231                         }
232                         else
233                         {
234                                 result = testDomain.Run( collector );
235                         }
236
237                         Directory.SetCurrentDirectory( savedDirectory );
238                         
239                         Console.WriteLine();
240                         Console.WriteLine();
241                         collector.PrintSummary( result );
242                         Console.WriteLine();
243
244                         string xmlOutput = CreateXmlOutput( result );
245                         
246                         if (options.xmlConsole)
247                                 Console.WriteLine(xmlOutput);
248                         else
249                                 CreateSummaryDocument(xmlOutput, transformReader, outStream);
250
251                         // Write xml output here
252                         string xmlResultFile = options.IsXml ? options.xml : "TestResult.xml";
253
254                         using ( StreamWriter writer = new StreamWriter( xmlResultFile ) ) 
255                         {
256                                 writer.Write(xmlOutput);
257                         }
258                         outStream.Flush();
259                         errorStream.Flush();
260
261                         if ( testDomain != null )
262                                 testDomain.Unload();
263
264                         return result.IsFailure ? 1 : 0;
265                 }
266
267                 private string CreateXmlOutput( TestResult result )
268                 {
269                         StringBuilder builder = new StringBuilder();
270                         XmlResultVisitor resultVisitor = new XmlResultVisitor(new StringWriter( builder ), result);
271                         result.Accept(resultVisitor);
272                         resultVisitor.Write();
273
274                         return builder.ToString();
275                 }
276
277                 private void CreateSummaryDocument(string xmlOutput, XmlTextReader transformReader,
278                                                    ConsoleWriter outStream)
279                 {
280                         XPathDocument originalXPathDocument = new XPathDocument(new StringReader(xmlOutput));
281                         XslTransform summaryXslTransform = new XslTransform();
282                         
283                         // Using obsolete form for now, remove warning suppression from project after changing
284                         summaryXslTransform.Load(transformReader);
285                         
286                         // Using obsolete form for now, remove warning suppression from project after changing
287                         summaryXslTransform.Transform(originalXPathDocument,null,outStream);
288                 }
289
290                 #region Nested Class to Handle Events
291
292                 private class EventCollector : LongLivingMarshalByRefObject, EventListener
293                 {
294                         private int testRunCount;
295                         private int testIgnoreCount;
296                         private int failureCount;
297                         private int level;
298
299                         private ConsoleOptions options;
300                         private ConsoleWriter writer;
301
302                         StringCollection messages;
303                 
304                         private bool debugger = false;
305                         private string currentTestName;
306
307                         public EventCollector( ConsoleOptions options, ConsoleWriter writer )
308                         {
309                                 debugger = Debugger.IsAttached;
310                                 level = 0;
311                                 this.options = options;
312                                 this.writer = writer;
313                                 this.currentTestName = string.Empty;
314                         }
315
316                         public void RunStarted(Test[] tests)
317                         {
318                         }
319
320                         public void RunFinished(TestResult[] results)
321                         {
322                         }
323
324                         public void RunFinished(Exception exception)
325                         {
326                         }
327
328                         public void TestFinished(TestCaseResult testResult)
329                         {
330                                 if ( !options.xmlConsole && !options.labels )
331                                 {
332                                         if(testResult.Executed)
333                                         {
334                                                 testRunCount++;
335                                                 
336                                                 if(testResult.IsFailure)
337                                                 {       
338                                                         failureCount++;
339                                                         Console.Write("F");
340                                                         if ( debugger )
341                                                         {
342                                                                 messages.Add( string.Format( "{0}) {1} :", failureCount, testResult.Test.FullName ) );
343                                                                 messages.Add( testResult.Message.Trim( Environment.NewLine.ToCharArray() ) );
344
345                                                                 string stackTrace = StackTraceFilter.Filter( testResult.StackTrace );
346                                                                 string[] trace = stackTrace.Split( System.Environment.NewLine.ToCharArray() );
347                                                                 foreach( string s in trace )
348                                                                 {
349                                                                         if ( s != string.Empty )
350                                                                         {
351                                                                                 string link = Regex.Replace( s.Trim(), @".* in (.*):line (.*)", "$1($2)");
352                                                                                 messages.Add( string.Format( "at\n{0}", link ) );
353                                                                         }
354                                                                 }
355                                                         }
356                                                 }
357                                         }
358                                         else
359                                         {
360                                                 testIgnoreCount++;
361                                                 Console.Write("N");
362                                         }
363                                 }
364
365                                 currentTestName = string.Empty;
366                         }
367
368                         public void TestStarted(TestCase testCase)
369                         {
370                                 currentTestName = testCase.FullName;
371
372                                 if ( options.labels )
373                                         writer.WriteLine("***** {0}", testCase.FullName );
374                                 else if ( !options.xmlConsole )
375                                         Console.Write(".");
376 }
377
378                         public void SuiteStarted(TestSuite suite) 
379                         {
380                                 if ( debugger && level++ == 0 )
381                                 {
382                                         messages = new StringCollection();
383                                         testRunCount = 0;
384                                         testIgnoreCount = 0;
385                                         failureCount = 0;
386                                         Trace.WriteLine( "################################ UNIT TESTS ################################" );
387                                         Trace.WriteLine( "Running tests in '" + suite.FullName + "'..." );
388                                 }
389                         }
390
391                         public void SuiteFinished(TestSuiteResult suiteResult) 
392                         {
393                                 if ( debugger && --level == 0) 
394                                 {
395                                         Trace.WriteLine( "############################################################################" );
396
397                                         if (messages.Count == 0) 
398                                         {
399                                                 Trace.WriteLine( "##############                 S U C C E S S               #################" );
400                                         }
401                                         else 
402                                         {
403                                                 Trace.WriteLine( "##############                F A I L U R E S              #################" );
404                                                 
405                                                 foreach ( string s in messages ) 
406                                                 {
407                                                         Trace.WriteLine(s);
408                                                 }
409                                         }
410
411                                         Trace.WriteLine( "############################################################################" );
412                                         Trace.WriteLine( "Executed tests : " + testRunCount );
413                                         Trace.WriteLine( "Ignored tests  : " + testIgnoreCount );
414                                         Trace.WriteLine( "Failed tests   : " + failureCount );
415                                         Trace.WriteLine( "Total time     : " + suiteResult.Time + " seconds" );
416                                         Trace.WriteLine( "############################################################################");
417                                 }
418                         }
419
420                         public void PrintSummary (TestResult suiteResult)
421                         {
422                                 if (failureCount > 0){
423                                         Console.WriteLine("Tests run: {0}, Failures: {1}, Not run: {2}, Time: {3} seconds",
424                                                           testRunCount, failureCount, testIgnoreCount, suiteResult.Time);
425                                 } else {
426                                         Console.WriteLine("Tests run: {0} (all pass), Not run: {1}, Time: {2} seconds",
427                                                           testRunCount, testIgnoreCount, suiteResult.Time);
428                                 }
429                         }
430
431                         public void UnhandledException( Exception exception )
432                         {
433                                 string msg = string.Format( "##### Unhandled Exception while running {0}", currentTestName );
434
435                                 // If we do labels, we already have a newline
436                                 if ( !options.labels ) writer.WriteLine();
437                                 writer.WriteLine( msg );
438                                 writer.WriteLine( exception.ToString() );
439
440                                 if ( debugger )
441                                 {
442                                         Trace.WriteLine( msg );
443                                         Trace.WriteLine( exception.ToString() );
444                                 }
445                         }
446                 }
447
448                 #endregion
449         }
450 }
451