// 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)
// Gert Driesen (gert.driesen@ardatis.com)
using System;
using System.Globalization;
using System.Runtime.Remoting;
using NAnt.Core;
using NAnt.Core.Attributes;
using NAnt.NUnit.Types;
using NAnt.NUnit1.Types;
namespace NAnt.NUnit1.Tasks {
///
/// Runs tests using the NUnit V1.0 framework.
///
///
///
/// See the NUnit home page for more
/// information.
///
///
/// The or
/// attributes are only used to stop more than one test suite to stop
/// running. If any test suite fails a build error will be thrown.
/// Set to to
/// ignore test errors and continue build.
///
///
///
///
/// Run tests in the MyProject.Tests.dll assembly.
///
///
/// The test results are logged in results.xml and results.txt
/// using the and
/// formatters, respectively.
///
///
///
///
///
///
///
/// ]]>
///
///
[TaskName("nunit")]
[Obsolete("In a future release, this task will be moved to NAntContrib. However, we strongly advise you to upgrade to NUnit 2.x.")]
public class NUnitTask : Task {
#region Private Instance Fields
private bool _haltOnError = false;
private bool _haltOnFailure = false;
private int _timeout = 0;
private bool _failuresPresent = false;
private bool _errorsPresent = false;
private NUnitTestCollection _tests = new NUnitTestCollection();
private FormatterElementCollection _formatterElements = new FormatterElementCollection();
#endregion Private Instance Fields
#region Public Instance Properties
///
/// Stops running tests when a test causes an error. The default is
/// .
///
///
/// Implies haltonfailure.
///
[TaskAttribute("haltonerror")]
[BooleanValidator()]
public bool HaltOnError {
get { return _haltOnError; }
set { _haltOnError = value; }
}
///
/// Stops running tests if a test fails (errors are considered failures
/// as well). The default is .
///
[TaskAttribute("haltonfailure")]
[BooleanValidator()]
public bool HaltOnFailure {
get { return _haltOnFailure; }
set { _haltOnFailure = value; }
}
///
/// Cancel the individual tests if they do not finish in the specified
/// time (measured in milliseconds). Ignored if fork is disabled.
///
[TaskAttribute("timeout")]
public int Timeout {
get { return _timeout; }
set { _timeout = value; }
}
///
/// Tests to run.
///
[BuildElementArray("test")]
public NUnitTestCollection Tests {
get { return _tests; }
}
///
/// Formatters to output results of unit tests.
///
[BuildElementArray("formatter")]
public FormatterElementCollection FormatterElements {
get { return _formatterElements; }
}
#endregion Public Instance Properties
#region Override implementation of Task
protected override void ExecuteTask() {
foreach (NUnitTest test in _tests) {
ExecuteTest(test);
}
if (_failuresPresent) {
throw new BuildException("Unit test failed, see build log.", Location);
}
if (_errorsPresent) {
throw new BuildException("Unit test had errors, see build log.", Location);
}
}
#endregion Override implementation of Task
#region Private Instance Methods
private void ExecuteTest(NUnitTest test) {
// Set Defaults
RunnerResult result = RunnerResult.Success;
if (test.ToDir == null) {
test.ToDir = Project.BaseDirectory;
}
if (test.OutFile == null) {
test.OutFile = "TEST-" + test.Class;
}
NUnitTestData testData = test.GetTestData();
foreach (FormatterElement element in FormatterElements) {
testData.Formatters.Add(element.Data);
}
if (testData.Fork == true) {
result = ExecuteInAppDomain(testData);
} else {
result = ExecuteInProc(testData);
}
// Handle return code:
// If there is an error/failure and that it should halt, stop
// everything otherwise just log a statement.
bool errorOccurred = (result == RunnerResult.Errors);
bool failureOccurred = (result != RunnerResult.Success);
if ((errorOccurred && test.HaltOnError) || (failureOccurred && test.HaltOnFailure)) {
// Only thrown if this test should halt as soon as the first
// error/failure is detected. In most cases all tests will
// be run to get a full list of problems.
throw new BuildException("Test " + testData.Class + " Failed" , Location);
}
// Used for reporting the final result from the task.
if (errorOccurred) {
_errorsPresent = true;
}
if (failureOccurred) {
_failuresPresent = true;
}
}
// TODO implement launching in a seperate App Domain
private RunnerResult ExecuteInAppDomain(NUnitTestData test) {
// spawn new domain in specified directory
AppDomainSetup domSetup = new AppDomainSetup();
domSetup.ApplicationBase = AppDomain.CurrentDomain.SetupInformation.ApplicationBase;
domSetup.ConfigurationFile = Project.GetFullPath(test.AppConfigFile);
domSetup.ApplicationName = "NAnt Remote Domain";
AppDomain newDomain = AppDomain.CreateDomain(domSetup.ApplicationName, AppDomain.CurrentDomain.Evidence, domSetup);
// instantiate subclassed test runner in new domain
Type runnerType = typeof(RemoteNUnitTestRunner);
ObjectHandle oh = newDomain.CreateInstance (
runnerType.Assembly.FullName,
runnerType.FullName,
false, 0, null,
new object[] { test },
null, null, null
);
RemoteNUnitTestRunner runner = (RemoteNUnitTestRunner)(oh.Unwrap());
Log(Level.Info, "Running '{0}'.", test.Class);
runner.Run(string.Empty, Verbose);
return runner.ResultCode;
}
private RunnerResult ExecuteInProc(NUnitTestData test) {
try {
NUnitTestRunner runner = new NUnitTestRunner(test);
if (runner.NeedsRunning()) {
Log(Level.Info, "Running '{0}'.", test.Class);
runner.Run(string.Empty, Verbose);
} else {
Log(Level.Info, "Skipping '{0}' because tests haven't changed.", test.Class);
}
return runner.ResultCode;
} catch (Exception ex) {
throw new BuildException("Error running unit test.", Location, ex);
}
}
#endregion Private Instance Methods
}
}