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