@@ -0,0 +1,315 @@
// NAnt - A .NET build tool
// Copyright (C) 2001-2005 Gert Driesen
//
// 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
//
// Troy Laurin (fiontan@westnet.com.au)

using System;

using NAnt.Core;
using NAnt.Core.Tasks;
using NAnt.Core.Attributes;
using NAnt.Core.Util;

namespace NAnt.Core.Tasks {
/// <summary>
/// Executes a set of tasks, and optionally catches a build exception to
/// allow recovery or rollback steps to be taken, or to define some steps
/// to be taken regardless if the tasks succeed or fail, or both.
/// </summary>
/// <remarks>
/// <para>
/// The tasks defined in the <c>&lt;<see cref="TryBlock" />&gt;</c> block
/// will be executed in turn, as they normally would in a target.
/// </para>
/// <para>
/// If a <c>&lt;<see cref="CatchBlock" />&gt;</c> block is defined, the
/// tasks in that block will be executed in turn only if one of the tasks
/// in the <c>&lt;<see cref="TryBlock" />&gt;</c> block fails. This
/// failure will then be suppressed by the <c>&lt;<see cref="CatchBlock" />&gt;</c>
/// block.
/// </para>
/// <para>
/// The message associated with the failure can also be caught in a
/// property for use within the <c>&lt;<see cref="CatchBlock" />&gt;</c>
/// block. The original contents of the property will be restored upon
/// exiting the <c>&lt;<see cref="CatchBlock" />&gt;</c> block.
/// </para>
/// <para>
/// If a <c>&lt;<see cref="FinallyBlock" />&gt;</c> block is defined, the
/// tasks in that block will be executed after the tasks in both the
/// <c>&lt;<see cref="TryBlock" />&gt;</c> and <c>&lt;<see cref="CatchBlock" />&gt;</c>
/// blocks have been executed, regardless of whether any task fails in
/// either block.
/// </para>
/// </remarks>
/// <example>
/// <code>
/// <![CDATA[
/// <trycatch>
/// <try>
/// <echo message="In try" />
/// <fail message="Failing!" />
/// </try>
/// <catch>
/// <echo message="In catch" />
/// </catch>
/// <finally>
/// <echo message="Finally done" />
/// </finally>
/// </trycatch>
/// ]]>
/// </code>
/// <para>
/// The output of this example will be:
/// </para>
/// <code>
/// In try
/// In catch
/// Finally done
/// </code>
/// <para>
/// The failure in the <c>&lt;<see cref="TryBlock" />&gt;</c> block will
/// not cause the build to fail.
/// </para>
/// </example>
/// <example>
/// <code>
/// <![CDATA[
/// <trycatch>
/// <try>
/// <echo message="In try" />
/// <fail message="Just because..." />
/// </try>
/// <catch property="failure">
/// <echo message="Caught failure: ${failure}" />
/// <fail message="Bad catch" />
/// </catch>
/// <finally>
/// <echo message="Finally done" />
/// </finally>
/// </trycatch>
/// ]]>
/// </code>
/// <para>
/// The output of this example will be:
/// </para>
/// <code>
/// In try
/// Caught failure: Just because...
/// Finally done
/// Build failed: Bad catch
/// </code>
/// <para>
/// Like the above, the failure in the <c>&lt;<see cref="TryBlock" />&gt;</c>
/// block does not cause the build to fail. The failure in the
/// <c>&lt;<see cref="CatchBlock" />&gt;</c> block does, however.
/// Note that the <c>&lt;<see cref="FinallyBlock" />&gt;</c> block is
/// executed even though the <c>&lt;<see cref="CatchBlock" />&gt;</c>
/// block failed.
/// </para>
/// </example>
/// <example>
/// <code>
/// <![CDATA[
/// <trycatch>
/// <try>
/// <echo message="In try" />
/// <fail message="yet again" />
/// </try>
/// <catch property="failure">
/// <echo message="Caught failure ${failure}" />
/// <fail message="Bad catch" />
/// </catch>
/// <finally>
/// <echo message="Finally done ${failure}" />
/// </finally>
/// </trycatch>
/// ]]>
/// </code>
/// <para>
/// The output of this example will be:
/// </para>
/// <code>
/// In try
/// Caught failure yet again
/// Build failed: Property 'failure' has not been set.
/// </code>
/// <para>
/// The <see cref="EchoTask" /> in the <c>&lt;<see cref="FinallyBlock" />&gt;</c>
/// block failed because the &quot;failure&quot; property was not defined
/// after exiting the <c>&lt;<see cref="CatchBlock" />&gt;</c> block.
/// Note that the failure in the <c>&lt;<see cref="FinallyBlock" />&gt;</c>
/// block has eclipsed the failure in the <c>&lt;<see cref="CatchBlock" />&gt;</c>
/// block.
/// </para>
/// </example>
/// <example>
/// <code>
/// <![CDATA[
/// <trycatch>
/// <try>
/// <property name="temp.file" value="${path::get-temp-file-name()}" />
/// <do-stuff to="${temp.file}" />
/// <fail message="Oops..." />
/// </try>
/// <finally>
/// <echo message="Cleaning up..." />
/// <if test="${property::exists('temp.file')}">
/// <delete file="${temp.file}" />
/// </if>
/// </finally>
/// </trycatch>
/// ]]>
/// </code>
/// <para>
/// A more concrete example, that will always clean up the generated
/// temporary file after it has been created.
/// </para>
/// </example>
[TaskName("trycatch")]
public class TryCatchTask : Task {
#region Private Instance Fields

private TaskContainer _tryBlock;
private CatchElement _catchBlock;
private TaskContainer _finallyBlock;

#endregion Private Instance Fields

#region Public Instance Properties

/// <summary>
/// The tasks in this block will be executed as a normal part of
/// the build script.
/// </summary>
[BuildElement("try", Required=true)]
public TaskContainer TryBlock {
get { return _tryBlock; }
set { _tryBlock = value; }
}

/// <summary>
/// The tasks in this block will be executed if any task in the try
/// block fails.
/// </summary>
[BuildElement("catch", Required=false)]
public CatchElement CatchBlock {
get { return _catchBlock; }
set { _catchBlock = value; }
}

/// <summary>
/// The tasks in this block will always be executed, regardless of
/// what happens in the try and catch blocks.
/// </summary>
/// <remarks>
/// Note that any failure in any of the tasks in this block will
/// prevent any subsequent tasks from executing.
/// </remarks>
[BuildElement("finally", Required=false)]
public TaskContainer FinallyBlock {
get { return _finallyBlock; }
set { _finallyBlock = value; }
}

#endregion Public Instance Properties

#region Override implementation of Task

protected override void ExecuteTask() {
try {
if (TryBlock != null) {
TryBlock.Execute();
}
} catch (BuildException be) {
if (CatchBlock != null) {
CatchBlock.Catch(be);
} else {
throw;
}
} finally {
if (FinallyBlock != null) {
FinallyBlock.Execute();
}
}
}

#endregion Override implementation of Task

public class CatchElement : TaskContainer {
#region Private Instance Fields

private string _property;

#endregion Private Instance Fields

#region Public Instance Properties

/// <summary>
/// Defines the name of the property to save the message describing
/// the failure that has been caught.
/// </summary>
/// <remarks>
/// <para>
/// The failure message is only available in the context of the catch
/// block. If you wish to preserve the message, you will need to save
/// it into another property.
/// </para>
/// <para>
/// Readonly properties cannot be overridden by this mechanism.
/// </para>
/// </remarks>
[TaskAttribute("property", Required=false)]
[StringValidator(AllowEmpty=false)]
public string Property {
get { return _property; }
set { _property = StringUtils.ConvertEmptyToNull(value); }
}

#endregion Public Instance Properties

#region Public Instance Methods

public void Catch(BuildException be) {
bool propertyExists = false;
string originalPropertyValue = null;

if (Property != null) {
propertyExists = Project.Properties.Contains(Property);
originalPropertyValue = Project.Properties[Property];
Project.Properties[Property] = be.RawMessage;
}

try {
Execute();
} finally {
if (Property != null) {
if (!propertyExists) {
// if the property did not exist, then remove it again
Project.Properties.Remove(Property);
} else {
// restore original value
Project.Properties[Property] = originalPropertyValue;
}
}
}
}

#endregion Public Instance Methods
}
}
}
@@ -0,0 +1,351 @@
//
// NAntContrib
// Copyright (C) 2001-2005 Gerry Shaw
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library 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
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// Gert Driesen (drieseng@users.sourceforge.net)

using System;
using System.IO;
using System.Xml;
using System.Xml.XPath;

using NUnit.Framework;

namespace Tests.NAnt.Core.Tasks {
[TestFixture]
public class ChooseTaskTest : BuildTestBase {
[Test]
public void Test_ConditionalExecution1() {
string _xml = @"
<project>
<choose>
<when test=""false"">
<patternset id=""when1.sources"">
<include name=""**/*.cs"" />
<exclude name=""**/*Test*"" />
</patternset>
<copy todir=""."">
<fileset>
<patternset refid=""when1.sources"" />
</fileset>
</copy>
<property name=""when1"" value=""executed"" />
</when>
<when test=""true"">
<property name=""when2"" value=""a"" />
<patternset id=""when2.sources"">
<include name=""**/*.cs"" />
<exclude name=""**/*Test*"" />
</patternset>
<property name=""when2"" value=""${when2}b"" />
<property name=""when2"" value=""${when2}c"" if=""${true == false}"" />
<copy todir=""."">
<fileset>
<patternset refid=""when2.sources"" />
</fileset>
</copy>
<property name=""when2"" value=""${when2}d"" />
</when>
<otherwise>
<patternset id=""otherwise.sources"">
<include name=""**/*.cs"" />
<exclude name=""**/*Test*"" />
</patternset>
<copy todir=""."">
<fileset>
<patternset refid=""otherwise.sources"" />
</fileset>
</copy>
<property name=""otherwise"" value=""executed"" />
</otherwise>
</choose>
<fail if=""${property::exists('when1')}"">#1</fail>
<fail unless=""${property::exists('when2')}"">#2</fail>
<fail unless=""${when2=='abd'}"">#3</fail>
<fail if=""${property::exists('otherwise')}"">#4</fail>
</project>";
RunBuild(_xml);
}

[Test]
public void Test_ConditionalExecution2() {
string _xml = @"
<project>
<choose>
<when test=""true"">
<property name=""when1"" value=""executed"" />
</when>
<when test=""false"">
<property name=""when2"" value=""executed"" />
</when>
</choose>
<fail unless=""${property::exists('when1')}"">#1</fail>
<fail if=""${property::exists('when2')}"">#2</fail>
</project>";
RunBuild(_xml);
}

[Test]
public void Test_ConditionalExecution3() {
string _xml = @"
<project>
<choose>
<when test=""false"">
<property name=""when1"" value=""executed"" />
</when>
<when test=""false"">
<property name=""when2"" value=""executed"" />
</when>
</choose>
<fail if=""${property::exists('when1')}"">#1</fail>
<fail if=""${property::exists('when2')}"">#2</fail>
</project>";
RunBuild(_xml);
}

[Test]
public void Test_Fallback() {
string _xml = @"
<project>
<choose>
<when test=""false"">
<property name=""when1"" value=""executed"" />
</when>
<otherwise>
<property name=""otherwise"" value=""executed"" />
</otherwise>
</choose>
<fail if=""${property::exists('when1')}"">#1</fail>
<fail unless=""${property::exists('otherwise')}"">#2</fail>
</project>";
RunBuild(_xml);
}

[Test]
[ExpectedException(typeof(TestBuildException))]
public void Test_ChildOrder1() {
string _xml = @"
<project>
<choose>
<when test=""false"">
<property name=""when1"" value=""executed"" />
</when>
<otherwise>
<property name=""otherwise"" value=""executed"" />
</otherwise>
<when test=""true"">
<property name=""when2"" value=""executed"" />
</when>
</choose>
</project>";
RunBuild(_xml);
}

[Test]
[ExpectedException(typeof(TestBuildException))]
public void Test_ChildOrder2() {
string _xml = @"
<project>
<choose>
<otherwise>
<property name=""otherwise"" value=""executed"" />
</otherwise>
<when test=""false"">
<property name=""when1"" value=""executed"" />
</when>
</choose>
</project>";
RunBuild(_xml);
}

[Test]
public void Test_EmptyWhenChild() {
string _xml = @"
<project>
<choose>
<when test=""true"" />
</choose>
</project>";
RunBuild(_xml);
}

[Test]
public void Test_EmptyOtherwiseChild() {
string _xml = @"
<project>
<choose>
<when test=""false"" />
<otherwise />
</choose>
</project>";
RunBuild(_xml);
}

[Test]
[ExpectedException(typeof(TestBuildException))]
public void Test_MissingWhenChild1() {
string _xml = @"
<project>
<choose />
</project>";
RunBuild(_xml);
}

[Test]
[ExpectedException(typeof(TestBuildException))]
public void Test_MissingWhenChild2() {
string _xml = @"
<project>
<choose>
<otherwise>
<property name=""otherwise"" value=""executed"" />
</otherwise>
</choose>
</project>";
RunBuild(_xml);
}

[Test]
[ExpectedException(typeof(TestBuildException))]
public void Test_InvalidWhenCondition() {
string _xml = @"
<project>
<choose>
<when test=""whatever"">
<property name=""when1"" value=""executed"" />
</when>
</choose>
</project>";
RunBuild(_xml);
}

[Test]
[ExpectedException(typeof(TestBuildException))]
public void Test_MissingWhenCondition() {
string _xml = @"
<project>
<choose>
<when>
<property name=""when1"" value=""executed"" />
</when>
</choose>
</project>";
RunBuild(_xml);
}

[Test]
[ExpectedException(typeof(TestBuildException))]
public void Test_EmptyWhenCondition() {
string _xml = @"
<project>
<choose>
<when test="""">
<property name=""when1"" value=""executed"" />
</when>
</choose>
</project>";
RunBuild(_xml);
}

[Test]
[ExpectedException(typeof(TestBuildException))]
public void Test_InvalidChild() {
string _xml = @"
<project>
<choose>
<when test=""true"">
<property name=""when1"" value=""executed"" />
</when>
<if test=""true"" />
</choose>
</project>";
RunBuild(_xml);
}

[Test]
[ExpectedException(typeof(TestBuildException))]
public void Test_InvalidExtension() {
string _xml = @"
<project>
<choose>
<when test=""true"">
<doesnotexist />
</when>
</choose>
</project>";
RunBuild(_xml);
}

[Test]
[ExpectedException(typeof(TestBuildException))]
public void Test_InvalidWhenParameter() {
string _xml = @"
<project>
<choose>
<when test=""true"" if=""true"">
<property name=""when1"" value=""executed"" />
</when>
</choose>
</project>";
RunBuild(_xml);
}

[Test]
[ExpectedException(typeof(TestBuildException))]
public void Test_InvalidOtherwiseParameter() {
string _xml = @"
<project>
<choose>
<when test=""true"">
<property name=""when1"" value=""executed"" />
</when>
<otherwise if=""true"">
<property name=""otherwise"" value=""executed"" />
</otherwise>
</choose>
</project>";
RunBuild(_xml);
}

[Test]
public void Test_FailOnError_False() {
string _xml = @"
<project>
<choose failonerror=""false"">
<when test=""true"">
<fail>Some reason</fail>
</when>
</choose>
</project>";
RunBuild(_xml);
}

[Test]
[ExpectedException(typeof(TestBuildException))]
public void Test_FailOnError_True() {
string _xml = @"
<project>
<choose>
<when test=""true"">
<fail>Some reason</fail>
</when>
</choose>
</project>";
RunBuild(_xml);
}
}
}
@@ -0,0 +1,172 @@
// NAnt - A .NET build tool
// Copyright (C) 2001-2005 Gert Driesen
//
// 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
//
// Troy Laurin (fiontan@westnet.com.au)

using System;

using NUnit.Framework;

namespace Tests.NAnt.Core.Tasks {
/// <summary>
/// Tests the TryCatch task.
/// </summary>
[TestFixture]
public class TryCatchTaskTest : BuildTestBase {
[SetUp]
protected override void SetUp() {
base.SetUp();
}

[Test]
public void Test_CatchExceptionWithoutMessage() {
string _xml = @"
<project>
<trycatch>
<try>
<fail message='Exception text' />
</try>
<catch>
<echo message='Catch!' />
</catch>
</trycatch>
</project>";
string result = RunBuild(_xml);
Assert.IsTrue(result.IndexOf("Catch!") != -1, "Exception should have been caught");
}

[Test]
public void Test_CatchExceptionWithMessage() {
string _xml = @"
<project>
<trycatch>
<try>
<fail message='Exception text' />
</try>
<catch property='ex'>
<echo message='Catch: ${ex}' />
</catch>
</trycatch>
</project>";
string result = RunBuild(_xml);
Assert.IsTrue(result.IndexOf("Catch: Exception text") != -1, "Exception message should have been caught and displayed");
}

[Test]
public void Test_CatchWithoutFail() {
string _xml = @"
<project>
<trycatch>
<try>
<echo message='No exception' />
</try>
<catch>
<echo message='Catch!' />
</catch>
</trycatch>
</project>";
string result = RunBuild(_xml);
Assert.IsTrue(result.IndexOf("No exception") != -1, "Try block should have run");
Assert.IsTrue(result.IndexOf("Catch!") == -1, "Catch block shouldn't have run");
}

[Test]
public void Test_CatchExceptionAndFinally() {
string _xml = @"
<project>
<trycatch>
<try>
<fail message='Exception text' />
</try>
<catch>
<echo message='Catch!' />
</catch>
<finally>
<echo message='Finally!' />
</finally>
</trycatch>
</project>";
string result = RunBuild(_xml);
Assert.IsTrue(result.IndexOf("Catch!") != -1, "Exception should have been caught");
Assert.IsTrue(result.IndexOf("Finally!") != -1, "Finally block should have run");
}

[Test]
public void Test_CatchWithoutFailAndFinally() {
string _xml = @"
<project>
<trycatch>
<try>
<echo message='No exception' />
</try>
<catch>
<echo message='Catch!' />
</catch>
<finally>
<echo message='Finally!' />
</finally>
</trycatch>
</project>";
string result = RunBuild(_xml);
Assert.IsTrue(result.IndexOf("No exception") != -1, "Try block should have run");
Assert.IsTrue(result.IndexOf("Catch!") == -1, "Catch block shouldn't have run");
Assert.IsTrue(result.IndexOf("Finally!") != -1, "Finally block should have run");
}

[Test]
public void Test_PropertyScopePreset() {
string _xml = @"
<project>
<property name='ex' value='Original' />
<trycatch>
<try>
<fail message='Exception text' />
</try>
<catch property='ex'>
<echo message='Catch: ${ex}' />
</catch>
<finally>
<echo message='Finally: ${ex}' />
</finally>
</trycatch>
</project>";
string result = RunBuild(_xml);
Assert.IsTrue(result.IndexOf("Catch: Exception text") != -1, "Exception message should have been caught and displayed");
Assert.IsTrue(result.IndexOf("Finally: Original") != -1, "Exception property should be reset outside the catch block");
}

[Test]
[ExpectedException(typeof(TestBuildException))]
public void Test_MessagePropertyScopeEmpty() {
string _xml = @"
<project>
<trycatch>
<try>
<fail message='Exception text' />
</try>
<catch property='ex'>
<echo message='Catch: ${ex}' />
</catch>
<finally>
<echo message='Finally: ${ex}' />
</finally>
</trycatch>
</project>";
RunBuild(_xml);
}
}
}
@@ -166,6 +166,8 @@
<Compile Include="NAnt.VSNet\Tasks\VjsSolutionTests.cs" />
<None Include="NAnt.Win32\NAnt.Win32.build" />
<Compile Include="NAnt.Win32\Tasks\ReadRegistryTest.cs" />
<Compile Include="NAnt.Core\Tasks\ChooseTaskTests.cs" />
<Compile Include="NAnt.Core\Tasks\TryCatchTaskTest.cs" />
</ItemGroup>
<ItemGroup>
<BootstrapperPackage Include="Microsoft.Net.Client.3.5">