// *********************************************************************** // Copyright (c) 2007 Charlie Poole // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** using System; using System.IO; using System.Collections; using System.Reflection; using NUnit.Framework.Api; using NUnit.Framework.Internal; using NUnit.Framework.Internal.Filters; using System.Diagnostics; namespace NUnitLite.Runner { /// /// TextUI is a general purpose class that runs tests and /// outputs to a TextWriter. /// /// Call it from your Main like this: /// new TextUI(textWriter).Execute(args); /// OR /// new TextUI().Execute(args); /// The provided TextWriter is used by default, unless the /// arguments to Execute override it using -out. The second /// form uses the Console, provided it exists on the platform. /// /// NOTE: When running on a platform without a Console, such /// as Windows Phone, the results will simply not appear if /// you fail to specify a file in the call itself or as an option. /// public class TextUI : ITestListener { private CommandLineOptions commandLineOptions; private NUnit.ObjectList assemblies = new NUnit.ObjectList(); private TextWriter writer; private ITestListener listener; private ITestAssemblyRunner runner; private FinallyDelegate finallyDelegate = new FinallyDelegate(); public bool Failure; #region Constructors /// /// Initializes a new instance of the class. /// public TextUI() : this(ConsoleWriter.Out, TestListener.NULL) { } /// /// Initializes a new instance of the class. /// /// The TextWriter to use. public TextUI(TextWriter writer) : this(writer, TestListener.NULL) { } /// /// Initializes a new instance of the class. /// /// The TextWriter to use. /// The Test listener to use. public TextUI(TextWriter writer, ITestListener listener) { // Set the default writer - may be overridden by the args specified this.writer = writer; this.listener = listener; } void TopLevelHandler(object sender, UnhandledExceptionEventArgs e) { // Make sure that the test harness knows this exception was thrown this.finallyDelegate.HandleUnhandledExc(e.ExceptionObject as Exception); } ITestAssemblyRunner DefaultRunner () { return new NUnitLiteTestAssemblyRunner(new NUnitLiteTestAssemblyBuilder(), this.finallyDelegate);; } #if MONODROID_TOOLS ITestAssemblyRunner AndroidRunner (string app) { return new Xamarin.AndroidTestAssemblyRunner(app); } #elif MONOTOUCH_TOOLS ITestAssemblyRunner iOSRunner (string app) { throw new NotImplementedException (); } #elif WASM_TOOLS ITestAssemblyRunner WebAssemblyRunner (string app) { throw new NotImplementedException (); } #endif #endregion #region Public Methods /// /// Execute a test run based on the aruments passed /// from Main. /// /// An array of arguments public void Execute(string[] args) { this.commandLineOptions = new CommandLineOptions(); commandLineOptions.Parse(args); if (runner == null) { #if MONODROID_TOOLS if (!string.IsNullOrEmpty (commandLineOptions.Android)) { runner = AndroidRunner (commandLineOptions.Android); } else if (!string.IsNullOrEmpty (commandLineOptions.Remote) && commandLineOptions.Remote.StartsWith ("android:")) { Xamarin.AndroidRemoteRunner.App = commandLineOptions.Remote.Substring (8); runner = DefaultRunner (); } else #elif MONOTOUCH_TOOLS if (!string.IsNullOrEmpty (commandLineOptions.iOS)) { runner = iOSRunner (commandLineOptions.iOS); } else if (!string.IsNullOrEmpty (commandLineOptions.Remote) && commandLineOptions.Remote.StartsWith ("ios:")) { // Xamarin.iOSRemoteRunner.App = commandLineOptions.Remote.Substring (4); // runner = DefaultRunner (); throw new NotImplementedException (); } else #elif WASM_TOOLS if (!string.IsNullOrEmpty (commandLineOptions.WebAssembly)) { runner = WebAssemblyRunner (commandLineOptions.WebAssembly); } else if (!string.IsNullOrEmpty (commandLineOptions.Remote) && commandLineOptions.Remote.StartsWith ("wasm:")) { // Xamarin.WebAssemblyRemoteRunner.App = commandLineOptions.Remote.Substring (5); // runner = DefaultRunner (); throw new NotImplementedException (); } else #endif { runner = DefaultRunner (); } } if (commandLineOptions.OutFile != null) this.writer = new StreamWriter(commandLineOptions.OutFile); if (!commandLineOptions.NoHeader) WriteHeader(this.writer); if (commandLineOptions.ShowHelp) writer.Write(commandLineOptions.HelpText); else if (commandLineOptions.Error) { writer.WriteLine(commandLineOptions.ErrorMessage); writer.WriteLine(commandLineOptions.HelpText); } else { WriteRuntimeEnvironment(this.writer); if (commandLineOptions.Wait && commandLineOptions.OutFile != null) writer.WriteLine("Ignoring /wait option - only valid for Console"); #if SILVERLIGHT IDictionary loadOptions = new System.Collections.Generic.Dictionary(); #else IDictionary loadOptions = new Hashtable(); #endif //if (options.Load.Count > 0) // loadOptions["LOAD"] = options.Load; //IDictionary runOptions = new Hashtable(); //if (commandLineOptions.TestCount > 0) // runOptions["RUN"] = commandLineOptions.Tests; ITestFilter filter = commandLineOptions.TestCount > 0 ? new SimpleNameFilter(commandLineOptions.Tests) : TestFilter.Empty; try { foreach (string name in commandLineOptions.Parameters) { try { assemblies.Add(Assembly.LoadFrom(name)); } catch (FileNotFoundException/* e*/) { assemblies.Add(Assembly.Load(name)); } } if (assemblies.Count == 0) { // NOTE: Execute must be directly called from the // test assembly in order for the mechanism to work. Assembly callingAssembly = Assembly.GetCallingAssembly(); assemblies.Add(callingAssembly); } // TODO: For now, ignore all but first assembly Assembly assembly = assemblies[0] as Assembly; Randomizer.InitialSeed = commandLineOptions.InitialSeed; if (!runner.Load(assembly, loadOptions)) { AssemblyName assemblyName = AssemblyHelper.GetAssemblyName(assembly); Console.WriteLine("No tests found in assembly {0}", assemblyName.Name); return; } if (commandLineOptions.Explore) ExploreTests(); else { if (commandLineOptions.Include != null && commandLineOptions.Include != string.Empty) { TestFilter includeFilter = new SimpleCategoryExpression(commandLineOptions.Include).Filter; if (filter.IsEmpty) filter = includeFilter; else filter = new AndFilter(filter, includeFilter); } if (commandLineOptions.Exclude != null && commandLineOptions.Exclude != string.Empty) { TestFilter excludeFilter = new NotFilter(new SimpleCategoryExpression(commandLineOptions.Exclude).Filter); if (filter.IsEmpty) filter = excludeFilter; else if (filter is AndFilter) ((AndFilter)filter).Add(excludeFilter); else filter = new AndFilter(filter, excludeFilter); } #if MONO filter = Xamarin.BabysitterSupport.AddBabysitterFilter(filter); #endif RunTests(filter); } } catch (FileNotFoundException ex) { Failure = true; writer.WriteLine(ex.Message); } catch (Exception ex) { Failure = true; writer.WriteLine(ex.ToString()); } finally { if (commandLineOptions.OutFile == null) { if (commandLineOptions.Wait) { Console.WriteLine("Press Enter key to continue . . ."); Console.ReadLine(); } } else { writer.Close(); } } } } /// /// Write the standard header information to a TextWriter. /// /// The TextWriter to use public static void WriteHeader(TextWriter writer) { Assembly executingAssembly = Assembly.GetExecutingAssembly(); #if NUNITLITE string title = "NUnitLite"; #else string title = "NUNit Framework"; #endif AssemblyName assemblyName = AssemblyHelper.GetAssemblyName(executingAssembly); System.Version version = assemblyName.Version; string copyright = "Copyright (C) 2012, Charlie Poole"; string build = ""; object[] attrs = executingAssembly.GetCustomAttributes(typeof(AssemblyTitleAttribute), false); if (attrs.Length > 0) { AssemblyTitleAttribute titleAttr = (AssemblyTitleAttribute)attrs[0]; title = titleAttr.Title; } attrs = executingAssembly.GetCustomAttributes(typeof(AssemblyCopyrightAttribute), false); if (attrs.Length > 0) { AssemblyCopyrightAttribute copyrightAttr = (AssemblyCopyrightAttribute)attrs[0]; copyright = copyrightAttr.Copyright; } attrs = executingAssembly.GetCustomAttributes(typeof(AssemblyConfigurationAttribute), false); if (attrs.Length > 0) { AssemblyConfigurationAttribute configAttr = (AssemblyConfigurationAttribute)attrs[0]; if (configAttr.Configuration.Length > 0) build = string.Format("({0})", configAttr.Configuration); } writer.WriteLine(String.Format("{0} {1} {2}", title, version.ToString(3), build)); writer.WriteLine(copyright); writer.WriteLine(); } /// /// Write information about the current runtime environment /// /// The TextWriter to be used public static void WriteRuntimeEnvironment(TextWriter writer) { string clrPlatform = Type.GetType("Mono.Runtime", false) == null ? ".NET" : "Mono"; writer.WriteLine("Runtime Environment -"); writer.WriteLine(" OS Version: {0}", Environment.OSVersion); writer.WriteLine(" {0} Version: {1}", clrPlatform, Environment.Version); writer.WriteLine(); } #endregion #region Helper Methods private void RunTests(ITestFilter filter) { DateTime startTime = DateTime.Now; var crashHandler = new UnhandledExceptionEventHandler(TopLevelHandler); AppDomain.CurrentDomain.UnhandledException += crashHandler; ITestResult result = runner.Run(this, filter); new ResultReporter(result, writer).ReportResults(); string resultFile = commandLineOptions.ResultFile; string resultFormat = commandLineOptions.ResultFormat; this.Failure = (result.ResultState.Equals(ResultState.Failure) || result.ResultState.Equals(ResultState.Error)); if (resultFile != null || commandLineOptions.ResultFormat != null) { if (resultFile == null) resultFile = "TestResult.xml"; if (resultFormat == "nunit2") new NUnit2XmlOutputWriter(startTime).WriteResultFile(result, resultFile); else if (resultFormat == "nunit3") new NUnit3XmlOutputWriter(startTime).WriteResultFile(result, resultFile); #if MONO else if (resultFormat == "xunit") new XunitXmlOutputWriter(startTime).WriteResultFile(result, resultFile); #endif else throw new Exception("Unknown resultFormat."); Console.WriteLine(); Console.WriteLine("Results saved as {0}.", resultFile); } AppDomain.CurrentDomain.UnhandledException -= crashHandler; } private void ExploreTests() { XmlNode testNode = runner.LoadedTest.ToXml(true); string listFile = commandLineOptions.ExploreFile; TextWriter textWriter = listFile != null && listFile.Length > 0 ? new StreamWriter(listFile) : Console.Out; #if CLR_2_0 || CLR_4_0 System.Xml.XmlWriterSettings settings = new System.Xml.XmlWriterSettings(); settings.Indent = true; settings.Encoding = System.Text.Encoding.UTF8; System.Xml.XmlWriter testWriter = System.Xml.XmlWriter.Create(textWriter, settings); #else System.Xml.XmlTextWriter testWriter = new System.Xml.XmlTextWriter(textWriter); testWriter.Formatting = System.Xml.Formatting.Indented; #endif testNode.WriteTo(testWriter); testWriter.Close(); Console.WriteLine(); Console.WriteLine("Test info saved as {0}.", listFile); } #endregion #region ITestListener Members /// /// A test has just started /// /// The test public void TestStarted(ITest test) { #if MONO if (!test.IsSuite) Xamarin.BabysitterSupport.RecordEnterTest(test.FullName); if (commandLineOptions.LabelTestsInOutput) writer.WriteLine("***** {0}", test.FullName); else writer.Write("."); #else if (commandLineOptions.LabelTestsInOutput) writer.WriteLine("***** {0}", test.Name); #endif } /// /// A test has just finished /// /// The result of the test public void TestFinished(ITestResult result) { #if MONO if (!result.Test.IsSuite) { Xamarin.BabysitterSupport.RecordLeaveTest (result.Test.FullName); if (result.ResultState.Status == TestStatus.Failed) { Xamarin.BabysitterSupport.RecordFailedTest (result.Test.FullName); if (!commandLineOptions.LabelTestsInOutput) writer.Write("F"); } } #endif } /// /// A test has produced some text output /// /// A TestOutput object holding the text that was written public void TestOutput(TestOutput testOutput) { } #endregion } }