// ***********************************************************************
// 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.Linq;
using System.Collections;
using System.Reflection;
using NUnit.Framework.Api;
using NUnit.Framework.Internal;
using NUnit.Framework.Internal.Filters;
using System.Diagnostics;
using NUnitLite.Runner;
using System.Net;
namespace GuiUnit
{
public class TestRunner : ITestListener
{
internal static MethodInfo LoadFileMethod;
static int ExitCode = 0;
static bool initialized = false;
static IMainLoopIntegration mainLoop;
public static event EventHandler BeforeShutdown;
static TestRunner ()
{
LoadFileMethod = typeof(Assembly).GetMethods ().FirstOrDefault (m => {
return m.Name == "LoadFile" && m.GetParameters ().Length == 1 && m.GetParameters () [0].ParameterType == typeof(string);
});
}
public static IMainLoopIntegration MainLoop {
get {
if (initialized)
return mainLoop;
initialized = true;
try { mainLoop = mainLoop ?? new XwtMainLoopIntegration (); } catch { }
try { mainLoop = mainLoop ?? new MonoMacMainLoopIntegration (); } catch { }
try { mainLoop = mainLoop ?? new GtkMainLoopIntegration (); } catch { }
return mainLoop;
} set {
mainLoop = value;
}
}
[STAThread]
public static int Main (string[] args)
{
new TestRunner ().Execute (args);
return ExitCode;
}
private CommandLineOptions commandLineOptions;
private NUnit.ObjectList assemblies = new NUnit.ObjectList();
private TextWriter writer;
private ITestListener listener;
private ITestAssemblyRunner runner;
#region Constructors
///
/// Initializes a new instance of the class.
///
public TestRunner() : this(ConsoleWriter.Out, TestListener.NULL) { }
///
/// Initializes a new instance of the class.
///
/// The TextWriter to use.
public TestRunner(TextWriter writer) : this(writer, TestListener.NULL) { }
///
/// Initializes a new instance of the class.
///
/// The TextWriter to use.
/// The Test listener to use.
public TestRunner(TextWriter writer, ITestListener listener)
{
// Set the default writer - may be overridden by the args specified
this.writer = writer;
this.runner = new NUnitLiteTestAssemblyRunner(new NUnitLiteTestAssemblyBuilder());
this.listener = listener;
}
#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 (commandLineOptions.OutFile != null)
this.writer = new StreamWriter(commandLineOptions.OutFile);
TcpWriter tcpWriter = null;
if (listener == TestListener.NULL && commandLineOptions.Port != -1) {
tcpWriter = new TcpWriter (new IPEndPoint (IPAddress.Loopback, commandLineOptions.Port));
listener = new XmlTestListener (tcpWriter);
}
// Ensure we always dispose the socket correctly.
using (tcpWriter)
ExecuteWithListener (args, tcpWriter);
}
void ExecuteWithListener (string[] args, TcpWriter tcpWriter)
{
// NOTE: Execute must be directly called from the
// test assembly in order for the mechanism to work.
Assembly callingAssembly = Assembly.GetCallingAssembly();
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
{
if (TestRunner.LoadFileMethod != null) {
foreach (string name in commandLineOptions.Parameters)
assemblies.Add (TestRunner.LoadFileMethod.Invoke (null, new[] { Path.GetFullPath (name) }));
}
if (assemblies.Count == 0)
assemblies.Add(callingAssembly);
// TODO: For now, ignore all but first assembly
Assembly assembly = assemblies[0] as Assembly;
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 (MainLoop == null) {
RunTests (filter);
} else {
MainLoop.InitializeToolkit ();
System.Threading.ThreadPool.QueueUserWorkItem (d => {
RunTests (filter);
Shutdown ();
});
MainLoop.RunMainLoop ();
}
}
}
catch (FileNotFoundException ex)
{
writer.WriteLine(ex.Message);
}
catch (Exception ex)
{
writer.WriteLine(ex.ToString());
}
finally
{
if (commandLineOptions.OutFile == null)
{
if (commandLineOptions.Wait)
{
Console.WriteLine("Press Enter key to continue . . .");
Console.ReadLine();
}
}
else
{
writer.Close();
}
}
}
}
static void Shutdown ()
{
// Run the shutdown method on the main thread
var helper = new InvokerHelper {
Func = () => {
if (BeforeShutdown != null)
BeforeShutdown (null, EventArgs.Empty);
MainLoop.Shutdown ();
return null;
}
};
MainLoop.InvokeOnMainLoop (helper);
}
///
/// 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)
{
ITestResult result = runner.Run(this, filter);
ExitCode = result.FailCount;
new ResultReporter(result, writer).ReportResults();
if (commandLineOptions.ResultFile != null)
{
new NUnit2XmlOutputWriter().WriteResultFile (result, commandLineOptions.ResultFile);
Console.WriteLine();
Console.WriteLine("Results saved as {0}.", commandLineOptions.ResultFile);
}
}
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 (commandLineOptions.LabelTestsInOutput)
writer.WriteLine("***** {0}", test.Name);
listener.TestStarted (test);
}
///
/// A test has just finished
///
/// The result of the test
public void TestFinished(ITestResult result)
{
listener.TestFinished (result);
}
///
/// A test has produced some text output
///
/// A TestOutput object holding the text that was written
public void TestOutput(TestOutput testOutput)
{
listener.TestOutput (testOutput);
}
#endregion
}
}