// NAnt - A .NET build tool
// Copyright (C) 2001-2003 Gerry Shaw
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// Ian MacLean (ian_maclean@another.com)
// Gerry Shaw (gerry_shaw@yahoo.com)
using System;
using System.Globalization;
using System.IO;
using System.Xml;
using NUnit.Framework;
using NUnit.Runner;
using NAnt.NUnit.Types;
using NAnt.NUnit1.Types;
namespace NAnt.NUnit1.Tasks {
public enum RunnerResult {
Success,
Failures,
Errors,
}
public class NUnitTestRunner : BaseTestRunner {
#region Public Instance Constructors
public NUnitTestRunner(NUnitTestData testData) {
_nunittest = testData;
string nunitsuite = testData.Class + "," + testData.Assembly;
_suite = GetSuite(nunitsuite);
testData.Suite = _suite;
}
#endregion Public Instance Constructors
#region Public Instance Properties
///
/// Gets the collection of registered formatters.
///
/// Collection of registered formatters.
public IResultFormatterCollection Formatters {
get { return _formatters; }
}
///
/// Gets the result of the test.
///
/// The result of the test.
public RunnerResult ResultCode {
get { return _resultCode; }
}
#endregion Public Instance Properties
#region Override implementation of BaseTestRunner
protected override void RunFailed(string message) {
}
#endregion Override implementation of BaseTestRunner
#region Override implementation of IListener
public override void AddError(ITest test, Exception t) {
foreach (IResultFormatter formatter in Formatters) {
formatter.AddError(test, t);
}
if (_nunittest.HaltOnError) {
_result.Stop();
}
}
public override void AddFailure(ITest test, AssertionFailedError t) {
foreach (IResultFormatter formatter in Formatters) {
formatter.AddFailure(test, t);
}
if (_nunittest.HaltOnFailure) {
_result.Stop();
}
}
public override void StartTest(ITest test) {
foreach (IResultFormatter formatter in Formatters) {
formatter.StartTest(test);
}
}
public override void EndTest(ITest test) {
foreach (IResultFormatter formatter in Formatters) {
formatter.EndTest(test);
}
}
#endregion Override implementation of IListener
#region Public Instance Methods
///
/// Determines if the unit test needs running.
///
///
/// if unit test needs running, otherwise,
/// .
///
///
///
/// Determines if the test needs running by looking at the date stamp
/// of the test assembly and the test results log.
///
///
public bool NeedsRunning() {
// assume we need to run unless proven otherwise
bool needsRunning = true;
string assemblyFileName = _nunittest.Assembly;
string logFileName = _nunittest.OutFile + ".xml";
if (File.Exists(logFileName) && File.Exists(assemblyFileName)) {
DateTime assemblyDateStamp = File.GetLastWriteTime(assemblyFileName);
DateTime logDataStamp = File.GetLastWriteTime(logFileName);
// simple check of datestamps normally works
if (logDataStamp > assemblyDateStamp) {
// date stamps are ok
// look inside results to see if there were failures or errors
try {
XmlDocument doc = new XmlDocument();
doc.Load(logFileName);
// check for errors or failures
//
int errors = Convert.ToInt32(doc.DocumentElement.Attributes["errors"].Value, NumberFormatInfo.InvariantInfo);
int failuers = Convert.ToInt32(doc.DocumentElement.Attributes["failures"].Value, NumberFormatInfo.InvariantInfo);
if (errors == 0 && failuers == 0) {
// no previous errors or failures and the assembly date stamp is older
// than the log so it should be safe to skip running the tests this time.
needsRunning = false;
}
} catch {
// some sort of error parsing xml, so just run the tests again
}
}
}
return needsRunning;
}
///
/// Runs a Suite extracted from a TestCase subclass.
///
public void Run(string logPrefix, bool verbose) {
CreateFormatters(_nunittest, logPrefix, verbose);
_result = new TestResultExtra();
_result.AddListener(this);
long startTime = System.DateTime.Now.Ticks;
// Handle start
OnStartTestSuite();
_suite.Run(_result);
// finished test
long endTime = System.DateTime.Now.Ticks;
long runTime = (endTime-startTime) / 10000;
_result.RunTime = runTime;
// Handle completion
OnEndTestSuite();
if (_result.WasSuccessful == false) {
if (_result.ErrorCount != 0) {
_resultCode = RunnerResult.Errors;
} else if (_result.FailureCount !=0) {
_resultCode = RunnerResult.Failures;
}
}
}
#endregion Public Instance Methods
#region Protected Instance Methods
///
/// Creates the formatters to be used when running this test.
///
protected void CreateFormatters(NUnitTestData testData, string logPrefix, bool verbose) {
// Now add the specified formatters
foreach (FormatterData formatterData in testData.Formatters) {
// determine file
FileInfo outFile = GetOutput(formatterData, testData);
IResultFormatter formatter = CreateFormatter(formatterData.Type, outFile);
Formatters.Add(formatter);
}
// Add default formatter
// The Log formatter is special in that it always writes to the
// Log class rather than the TextWriter set in SetOutput().
// HACK!
LogFormatter logFormatter = new LogFormatter(logPrefix, verbose);
Formatters.Add(logFormatter);
}
///
/// Returns the output file or null if does not use a file.
///
protected FileInfo GetOutput(FormatterData formatterData, NUnitTestData test) {
if (formatterData.UseFile) {
string filename = test.OutFile + formatterData.Extension;
string absFilename = Path.Combine(test.ToDir, filename);
return new FileInfo(absFilename);
}
return null;
}
protected IResultFormatter CreateFormatter(FormatterType type, FileInfo outfile) {
IResultFormatter retFormatter = null;
switch (type) {
case FormatterType.Plain:
retFormatter = (IResultFormatter) new PlainTextFormatter();
break;
case FormatterType.Xml:
retFormatter = (IResultFormatter) new XmlResultFormatter();
break;
default:
break;
}
if (outfile == null) {
// TO-DO : find solution for creating LogWriter without access to Task or Project
// for dispatching logging events
// retFormatter.SetOutput(new LogWriter());
retFormatter.SetOutput(Console.Out);
} else {
retFormatter.SetOutput(new StreamWriter(outfile.Create()));
}
return retFormatter;
}
#endregion Protected Instance Methods
#region Private Instance Methods
///
/// Returns the test suite from a given class.
///
///
/// The assemblyQualifiedName parameter needs to be in form:
/// "full.qualified.class.name,Assembly"
///
private ITest GetSuite(string assemblyQualifiedName) {
// Don't worry about catching exceptions in this method. The
// NUnitTask will catch them and throw a BuildException back to
// NAnt with the correct location in the build file. [gs]
StandardLoader loader = new StandardLoader();
ITest test = loader.LoadTest(assemblyQualifiedName);
return test;
}
private void OnStartTestSuite() {
foreach (IResultFormatter formatter in Formatters) {
formatter.StartTestSuite(_nunittest);
}
}
private void OnEndTestSuite() {
foreach (IResultFormatter formatter in Formatters) {
formatter.EndTestSuite(_result);
}
}
#endregion Private Instance Methods
#region Private Instance Fields
IResultFormatterCollection _formatters = new IResultFormatterCollection();
NUnitTestData _nunittest = null;
ITest _suite = null;
TestResultExtra _result = null;
RunnerResult _resultCode = RunnerResult.Success;
#endregion Private Instance Fields
}
}