Return-Path: Delivered-To: apmail-ant-dev-archive@www.apache.org Received: (qmail 79109 invoked from network); 29 Oct 2007 17:45:01 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (140.211.11.2) by minotaur.apache.org with SMTP; 29 Oct 2007 17:45:01 -0000 Received: (qmail 87064 invoked by uid 500); 29 Oct 2007 17:44:46 -0000 Delivered-To: apmail-ant-dev-archive@ant.apache.org Received: (qmail 87034 invoked by uid 500); 29 Oct 2007 17:44:46 -0000 Mailing-List: contact dev-help@ant.apache.org; run by ezmlm Precedence: bulk List-Unsubscribe: List-Help: List-Post: List-Id: "Ant Developers List" Reply-To: "Ant Developers List" Delivered-To: mailing list dev@ant.apache.org Received: (qmail 87023 invoked by uid 500); 29 Oct 2007 17:44:46 -0000 Received: (qmail 87020 invoked by uid 99); 29 Oct 2007 17:44:46 -0000 Received: from nike.apache.org (HELO nike.apache.org) (192.87.106.230) by apache.org (qpsmtpd/0.29) with ESMTP; Mon, 29 Oct 2007 10:44:46 -0700 X-ASF-Spam-Status: No, hits=-100.0 required=10.0 tests=ALL_TRUSTED X-Spam-Check-By: apache.org Received: from [140.211.11.3] (HELO eris.apache.org) (140.211.11.3) by apache.org (qpsmtpd/0.29) with ESMTP; Mon, 29 Oct 2007 17:45:04 +0000 Received: by eris.apache.org (Postfix, from userid 65534) id 5B3331A9832; Mon, 29 Oct 2007 10:44:32 -0700 (PDT) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r589767 - in /ant/core/trunk/src/main/org/apache/tools/ant: taskdefs/ taskdefs/optional/testing/ util/ Date: Mon, 29 Oct 2007 17:44:29 -0000 To: ant-cvs@apache.org From: stevel@apache.org X-Mailer: svnmailer-1.0.8 Message-Id: <20071029174432.5B3331A9832@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org Author: stevel Date: Mon Oct 29 10:44:21 2007 New Revision: 589767 URL: http://svn.apache.org/viewvc?rev=589767&view=rev Log: new test tasks, not declared yet -so they dont exist- for functional testing is waitfor that throws a BuildTimeoutException when it times out Added: ant/core/trunk/src/main/org/apache/tools/ant/taskdefs/optional/testing/ ant/core/trunk/src/main/org/apache/tools/ant/taskdefs/optional/testing/BlockFor.java ant/core/trunk/src/main/org/apache/tools/ant/taskdefs/optional/testing/BuildTimeoutException.java ant/core/trunk/src/main/org/apache/tools/ant/taskdefs/optional/testing/Funtest.java ant/core/trunk/src/main/org/apache/tools/ant/util/WorkerAnt.java Modified: ant/core/trunk/src/main/org/apache/tools/ant/taskdefs/WaitFor.java Modified: ant/core/trunk/src/main/org/apache/tools/ant/taskdefs/WaitFor.java URL: http://svn.apache.org/viewvc/ant/core/trunk/src/main/org/apache/tools/ant/taskdefs/WaitFor.java?rev=589767&r1=589766&r2=589767&view=diff ============================================================================== --- ant/core/trunk/src/main/org/apache/tools/ant/taskdefs/WaitFor.java (original) +++ ant/core/trunk/src/main/org/apache/tools/ant/taskdefs/WaitFor.java Mon Oct 29 10:44:21 2007 @@ -52,20 +52,24 @@ * @ant.task category="control" */ public class WaitFor extends ConditionBase { - private static final long ONE_SECOND = 1000L; - private static final long ONE_MINUTE = ONE_SECOND * 60L; - private static final long ONE_HOUR = ONE_MINUTE * 60L; - private static final long ONE_DAY = ONE_HOUR * 24L; - private static final long ONE_WEEK = ONE_DAY * 7L; - - private static final long DEFAULT_MAX_WAIT_MILLIS = ONE_MINUTE * 3L; - private static final long DEFAULT_CHECK_MILLIS = 500L; - - /** default max wait time */ - private long maxWaitMillis = DEFAULT_MAX_WAIT_MILLIS; - private long maxWaitMultiplier = 1L; - private long checkEveryMillis = DEFAULT_CHECK_MILLIS; - private long checkEveryMultiplier = 1L; + public static final long ONE_MILLISECOND = 1L; + public static final long ONE_SECOND = 1000L; + public static final long ONE_MINUTE = ONE_SECOND * 60L; + public static final long ONE_HOUR = ONE_MINUTE * 60L; + public static final long ONE_DAY = ONE_HOUR * 24L; + public static final long ONE_WEEK = ONE_DAY * 7L; + + public static final long DEFAULT_MAX_WAIT_MILLIS = ONE_MINUTE * 3L; + public static final long DEFAULT_CHECK_MILLIS = 500L; + + /** default max wait time in the current unit*/ + private long maxWait = DEFAULT_MAX_WAIT_MILLIS; + private long maxWaitMultiplier = ONE_MILLISECOND; + /** + * check time in the current unit + */ + private long checkEvery = DEFAULT_CHECK_MILLIS; + private long checkEveryMultiplier = ONE_MILLISECOND; private String timeoutProperty; /** @@ -75,14 +79,26 @@ super("waitfor"); } + + /** + * Constructor that takes the name of the task in the task name. + * + * @param taskName the name of the task. + * @since Ant 1.8 + */ + public WaitFor(String taskName) { + super(taskName); + } + /** * Set the maximum length of time to wait. * @param time a long value */ public void setMaxWait(long time) { - maxWaitMillis = time; + maxWait = time; } + /** * Set the max wait time unit * @param unit an enumerated Unit value @@ -91,12 +107,14 @@ maxWaitMultiplier = unit.getMultiplier(); } + + /** * Set the time between each check * @param time a long value */ public void setCheckEvery(long time) { - checkEveryMillis = time; + checkEvery = time; } /** @@ -131,32 +149,42 @@ + getTaskName()); } Condition c = (Condition) getConditions().nextElement(); - - long savedMaxWaitMillis = maxWaitMillis; - long savedCheckEveryMillis = checkEveryMillis; try { - try { - maxWaitMillis *= maxWaitMultiplier; - checkEveryMillis *= checkEveryMultiplier; - long start = System.currentTimeMillis(); - long end = start + maxWaitMillis; - - while (System.currentTimeMillis() < end) { - if (c.eval()) { - processSuccess(); - return; - } - Thread.sleep(checkEveryMillis); + long maxWaitMillis = calculateMaxWaitMillis(); + long checkEveryMillis = calculateCheckEveryMillis(); + long start = System.currentTimeMillis(); + long end = start + maxWaitMillis; + + while (System.currentTimeMillis() < end) { + if (c.eval()) { + processSuccess(); + return; } - } catch (InterruptedException e) { - log("Task " + getTaskName() - + " interrupted, treating as timed out."); + Thread.sleep(checkEveryMillis); } - processTimeout(); - } finally { - maxWaitMillis = savedMaxWaitMillis; - checkEveryMillis = savedCheckEveryMillis; + } catch (InterruptedException e) { + log("Task " + getTaskName() + + " interrupted, treating as timed out."); } + processTimeout(); + } + + /** + * Get the check wait time, in milliseconds. + * @since Ant 1.8 + * @return how long to wait between checks + */ + public long calculateCheckEveryMillis() { + return checkEvery * checkEveryMultiplier; + } + + /** + * Get the maxiumum wait time, in milliseconds. + * @since Ant 1.8 + * @return how long to wait before timing out + */ + public long calculateMaxWaitMillis() { + return maxWait * maxWaitMultiplier; } /** Added: ant/core/trunk/src/main/org/apache/tools/ant/taskdefs/optional/testing/BlockFor.java URL: http://svn.apache.org/viewvc/ant/core/trunk/src/main/org/apache/tools/ant/taskdefs/optional/testing/BlockFor.java?rev=589767&view=auto ============================================================================== --- ant/core/trunk/src/main/org/apache/tools/ant/taskdefs/optional/testing/BlockFor.java (added) +++ ant/core/trunk/src/main/org/apache/tools/ant/taskdefs/optional/testing/BlockFor.java Mon Oct 29 10:44:21 2007 @@ -0,0 +1,73 @@ +/* + * Copyright 2007 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.apache.tools.ant.taskdefs.optional.testing; + +import org.apache.tools.ant.taskdefs.WaitFor; + +/** + * + * Created 29-Oct-2007 12:28:28 + * @since Ant 1.8 + */ + +public class BlockFor extends WaitFor { + + /** + * Text to include in a message + */ + private String text; + + + /** + * Constructor that takes the name of the task in the task name. + * + */ + public BlockFor() { + super("blockfor"); + text=getTaskName()+" timed out"; + } + + /** + * Constructor that takes the name of the task in the task name. + * + * @param taskName the name of the task. + */ + public BlockFor(String taskName) { + super(taskName); + } + + /** + * If the wait fails, a BuildException is thrown. All the superclasses actions are called first. + * @throws BuildTimeoutException on timeout, using the text in {@link #text} + * + */ + protected void processTimeout() throws BuildTimeoutException { + super.processTimeout(); + throw new BuildTimeoutException(text,getLocation()); + } + + /** + * Set the error text; all properties are expanded in the message. + * + * @param message the text to use in a failure message + */ + public void addText(String message) { + text = getProject().replaceProperties(message); + } + + +} Added: ant/core/trunk/src/main/org/apache/tools/ant/taskdefs/optional/testing/BuildTimeoutException.java URL: http://svn.apache.org/viewvc/ant/core/trunk/src/main/org/apache/tools/ant/taskdefs/optional/testing/BuildTimeoutException.java?rev=589767&view=auto ============================================================================== --- ant/core/trunk/src/main/org/apache/tools/ant/taskdefs/optional/testing/BuildTimeoutException.java (added) +++ ant/core/trunk/src/main/org/apache/tools/ant/taskdefs/optional/testing/BuildTimeoutException.java Mon Oct 29 10:44:21 2007 @@ -0,0 +1,111 @@ +/* + * Copyright 2007 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.apache.tools.ant.taskdefs.optional.testing; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Location; + +/** + * + * This exception is used to indicate timeouts. + * @since Ant1.8 + * + */ + +public class BuildTimeoutException extends BuildException { + + + /** + * Constructs a build exception with no descriptive information. + */ + public BuildTimeoutException() { + } + + /** + * Constructs an exception with the given descriptive message. + * + * @param message A description of or information about the exception. + * Should not be null. + */ + public BuildTimeoutException(String message) { + super(message); + } + + /** + * Constructs an exception with the given message and exception as + * a root cause. + * + * @param message A description of or information about the exception. + * Should not be null unless a cause is specified. + * @param cause The exception that might have caused this one. + * May be null. + */ + public BuildTimeoutException(String message, Throwable cause) { + super(message, cause); + } + + /** + * Constructs an exception with the given message and exception as + * a root cause and a location in a file. + * + * @param msg A description of or information about the exception. + * Should not be null unless a cause is specified. + * @param cause The exception that might have caused this one. + * May be null. + * @param location The location in the project file where the error + * occurred. Must not be null. + */ + public BuildTimeoutException(String msg, Throwable cause, Location location) { + super(msg, cause, location); + } + + /** + * Constructs an exception with the given exception as a root cause. + * + * @param cause The exception that might have caused this one. + * Should not be null. + */ + public BuildTimeoutException(Throwable cause) { + super(cause); + } + + /** + * Constructs an exception with the given descriptive message and a + * location in a file. + * + * @param message A description of or information about the exception. + * Should not be null. + * @param location The location in the project file where the error + * occurred. Must not be null. + */ + public BuildTimeoutException(String message, Location location) { + super(message, location); + } + + /** + * Constructs an exception with the given exception as + * a root cause and a location in a file. + * + * @param cause The exception that might have caused this one. + * Should not be null. + * @param location The location in the project file where the error + * occurred. Must not be null. + */ + public BuildTimeoutException(Throwable cause, Location location) { + super(cause, location); + } +} Added: ant/core/trunk/src/main/org/apache/tools/ant/taskdefs/optional/testing/Funtest.java URL: http://svn.apache.org/viewvc/ant/core/trunk/src/main/org/apache/tools/ant/taskdefs/optional/testing/Funtest.java?rev=589767&view=auto ============================================================================== --- ant/core/trunk/src/main/org/apache/tools/ant/taskdefs/optional/testing/Funtest.java (added) +++ ant/core/trunk/src/main/org/apache/tools/ant/taskdefs/optional/testing/Funtest.java Mon Oct 29 10:44:21 2007 @@ -0,0 +1,457 @@ +/* + * Copyright 2007 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.tools.ant.taskdefs.optional.testing; + +import org.apache.tools.ant.Task; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.TaskAdapter; +import org.apache.tools.ant.util.WorkerAnt; +import org.apache.tools.ant.taskdefs.condition.Condition; +import org.apache.tools.ant.taskdefs.Parallel; +import org.apache.tools.ant.taskdefs.Sequential; +import org.apache.tools.ant.taskdefs.WaitFor; + +/** + * Task to provide functional testing under Ant, with a fairly complex worflow of: + * + *
    + *
  • Conditional execution
  • + *
  • Application to start
  • + *
  • A probe to "waitfor" before running tests
  • + *
  • A tests sequence
  • + *
  • A reporting sequence that runs after the tests have finished
  • + *
  • A "teardown" clause that runs after the rest.
  • + *
  • Automated termination of the program it executes, if a timeout is not met
  • + *
  • Checking of a failure property and automatic raising of a fault (with the text in failureText) + * if test shutdown and reporting succeeded
  • + *
+ * + * The task is designed to be framework neutral; it will work with JUnit, TestNG and other test frameworks That can be + * executed from Ant. It bears a resemblance to the FunctionalTest task from SmartFrog, as the attribute names were + * chosen to make migration easier. However, this task benefits from the ability to tweak Ant's internals, and so + * simplify the workflow, and from the experience of using the SmartFrog task. No code has been shared. + * + * @since Ant 1.8 + */ + +public class Funtest extends Task { + + /** + * A condition that must be true before the tests are run. This makes it easier to define complex tests that only + * run if certain conditions are met, such as OS or network state. + */ + private Condition condition; + + + /** + * Used internally to set the workflow up + */ + private Parallel timedTests; + + /** + * Setup runs if the condition is met. Once setup is complete, teardown will be run when the task finishes + */ + private Sequential setup; + + /** + * The application to run + */ + private Sequential application; + + /** + * A block that halts the tests until met. + */ + private BlockFor block; + + /** + * Tests to run + */ + private Sequential tests; + + /** + * Reporting only runs if the tests were executed. If the block stopped them, reporting is skipped. + */ + private Sequential reporting; + + /** + * Any teardown operations. + */ + private Sequential teardown; + + /** + * time for the tests to time out + */ + private long timeout; + + private long timeoutUnitMultiplier= WaitFor.ONE_MILLISECOND; + + /** + * time for the execution to time out. + */ + private long shutdownTime = 10*WaitFor.ONE_SECOND; + + private long shutdownUnitMultiplier = WaitFor.ONE_MILLISECOND; + + /** + * Name of a property to look for + */ + private String failureProperty; + + /** + * Message to send when tests failed + */ + private String failureMessage="Tests failed"; + + /** + * Flag to set to true if you don't care about any shutdown errors. + *

+ * In that situation, errors raised during teardown are logged but not + * turned into BuildFault events. Similar to catching and ignoring + * finally {} clauses in Java/ + */ + private boolean failOnTeardownErrors=true; + + + /** + * What was thrown in the test run (including reporting) + */ + private BuildException testException; + /** + * What got thrown during teardown + */ + private BuildException teardownException; + + /** + * Did the application throw an exception + */ + private BuildException applicationException; + + /** + * Did the task throw an exception + */ + private BuildException taskException; + + /** {@value} */ + public static final String WARN_OVERRIDING = "Overriding previous definition of "; + /** {@value} */ + public static final String APPLICATION_FORCIBLY_SHUT_DOWN = "Application forcibly shut down"; + /** {@value} */ + public static final String SHUTDOWN_INTERRUPTED = "Shutdown interrupted"; + public static final String SKIPPING_TESTS = "Condition failed -skipping tests"; + + /** + * Log if the definition is overriding something + * + * @param name what is being defined + * @param definition what should be null if you don't want a warning + */ + private void logOverride(String name, Object definition) { + if (definition != null) { + log(WARN_OVERRIDING + '<' + name + '>', Project.MSG_WARN); + } + } + + public void addCondition(Condition newCondition) { + logOverride("condition", condition); + condition = newCondition; + } + + public void addApplication(Sequential sequence) { + logOverride("application", application); + application = sequence; + } + + public void addSetup(Sequential sequence) { + logOverride("setup", setup); + setup = sequence; + } + + public void addBlock(BlockFor sequence) { + logOverride("block", block); + block = sequence; + } + + public void addTests(Sequential sequence) { + logOverride("tests", tests); + tests = sequence; + } + + public void addReporting(Sequential sequence) { + logOverride("reporting", reporting); + reporting = sequence; + } + + public void addTeardown(Sequential sequence) { + logOverride("teardown", teardown); + teardown = sequence; + } + + + public void setFailOnTeardownErrors(boolean failOnTeardownErrors) { + this.failOnTeardownErrors = failOnTeardownErrors; + } + + public void setFailureMessage(String failureMessage) { + this.failureMessage = failureMessage; + } + + public void setFailureProperty(String failureProperty) { + this.failureProperty = failureProperty; + } + + public void setShutdownTime(long shutdownTime) { + this.shutdownTime = shutdownTime; + } + + public void setTimeout(long timeout) { + this.timeout = timeout; + } + + public void setTimeoutUnit(WaitFor.Unit unit) { + timeoutUnitMultiplier=unit.getMultiplier(); + } + + public void setShutdownUnit(WaitFor.Unit unit) { + shutdownUnitMultiplier = unit.getMultiplier(); + } + + + public BuildException getApplicationException() { + return applicationException; + } + + public BuildException getTeardownException() { + return teardownException; + } + + public BuildException getTestException() { + return testException; + } + + public BuildException getTaskException() { + return taskException; + } + + /** + * Bind and initialise a task + * @param task task to bind + */ + private void bind(Task task) { + task.bindToOwner(this); + task.init(); + } + + /** + * Create a newly bound parallel instance + * @param parallelTimeout timeout + * @return a bound and initialised parallel instance. + */ + private Parallel newParallel(long parallelTimeout) { + Parallel par=new Parallel(); + bind(par); + par.setFailOnAny(true); + par.setTimeout(parallelTimeout); + return par; + } + + /** + * Create a newly bound parallel instance with one child + * @param parallelTimeout timeout + * @return a bound and initialised parallel instance. + */ + private Parallel newParallel(long parallelTimeout,Task child) { + Parallel par = newParallel(parallelTimeout); + par.addTask(child); + return par; + } + + /** + * Run the functional test sequence. + *

+ * This is a fairly complex workflow -what is going on is that we try to clean up + * no matter how the run ended, and to retain the innermost exception that got thrown + * during cleanup. That is, if teardown fails after the tests themselves failed, it is the + * test failing that is more important. + * @throws BuildException if something was caught during the run or teardown. + */ + public void execute() throws BuildException { + + //before anything else, check the condition + //and bail out if it is defined but not true + if (condition != null && !condition.eval()) { + //we are skipping the test + log(SKIPPING_TESTS); + return; + } + + long timeoutMillis = timeout * timeoutUnitMultiplier; + + //set up the application to run in a separate thread + Parallel applicationRun = newParallel(timeoutMillis); + //with a worker which we can use to manage it + WorkerAnt worker = new WorkerAnt(applicationRun, null); + if (application != null) { + applicationRun.addTask(application); + } + + //The test run consists of the block followed by the tests. + long testRunTimeout = 0; + Sequential testRun = new Sequential(); + bind(testRun); + if (block != null) { + //waitfor is not a task, it needs to be adapted + testRun.addTask(new TaskAdapter(block)); + //add the block time to the total test run timeout + testRunTimeout = block.calculateMaxWaitMillis(); + } + + //add the tests and more delay + if (tests != null) { + testRun.addTask(tests); + testRunTimeout += timeoutMillis; + } + //add the reporting and more delay + if (reporting != null) { + testRun.addTask(reporting); + testRunTimeout += timeoutMillis; + } + + //wrap this in a parallel purely to set up timeouts for the + //test run + timedTests = newParallel(testRunTimeout, testRun); + + try { + //run any setup task + if (setup != null) { + Parallel setupRun = newParallel(timeoutMillis, setup); + setupRun.execute(); + } + //start the worker thread and leave it running + worker.start(); + //start the probe+test sequence + timedTests.execute(); + } catch (BuildException e) { + //Record the exception and continue + testException = e; + } finally { + //teardown always runs; its faults are filed away + if (teardown != null) { + try { + Parallel teardownRun = newParallel(timeoutMillis, teardown); + teardownRun.execute(); + } catch (BuildException e) { + teardownException = e; + } + } + } + + //we get here whether or not the tests/teardown have thrown a BuildException. + //do a forced shutdown of the running application, before processing the faults + + try { + //wait for the worker to have finished + long shutdownTimeMillis = shutdownTime * shutdownUnitMultiplier; + worker.waitUntilFinished(shutdownTimeMillis); + if (worker.isAlive()) { + //then, if it is still running, interrupt it a second time. + log(APPLICATION_FORCIBLY_SHUT_DOWN, Project.MSG_WARN); + worker.interrupt(); + worker.waitUntilFinished(shutdownTimeMillis); + } + } catch (InterruptedException e) { + //success, something interrupted the shutdown. There may be a leaked + //worker; + log(SHUTDOWN_INTERRUPTED, e, Project.MSG_VERBOSE); + } + applicationException = worker.getBuildException(); + + /**Now faults are analysed + the priority is + -testexceptions, except those indicating a build timeout when the application itself + failed. + (because often it is the application fault that is more interesting than the probe + failure, which is usually triggered by the application not starting + -application exceptions (above test timeout exceptions) + -teardown exceptions -except when they are being ignored + -any + */ + + processExceptions(); + } + + /** + * Now faults are analysed. + *

The priority is + *

    + *
  1. testexceptions, except those indicating a build timeout when the application itself + failed.
    + (because often it is the application fault that is more interesting than the probe + failure, which is usually triggered by the application not starting +
  2. + Application exceptions (above test timeout exceptions) +
  3. + Teardown exceptions -except when they are being ignored +
  4. + Test failures as indicated by the failure property +
+ + */ + protected void processExceptions() { + taskException = testException; + + //look for an application fault + if (applicationException != null) { + if (taskException == null || taskException instanceof BuildTimeoutException) { + taskException = applicationException; + } else { + log("Application Exception:" + applicationException.toString(), + applicationException, + Project.MSG_WARN); + } + } + + //now look for teardown faults, which may be ignored + if (teardownException != null) { + if (taskException == null && failOnTeardownErrors) { + taskException = teardownException; + } else { + //don't let the cleanup exception get in the way of any other failure + log("teardown exception" + teardownException.toString(), + teardownException, + Project.MSG_WARN); + } + } + + //now, analyse the tests + if (failureProperty != null + && getProject().getProperty(failureProperty) != null) { + //we've failed + log(failureMessage); + if(taskException == null) { + taskException = new BuildException(failureMessage); + } + } + + //at this point taskException is null or not. + //if not, throw the exception + if (taskException != null) { + throw taskException; + } + } +} Added: ant/core/trunk/src/main/org/apache/tools/ant/util/WorkerAnt.java URL: http://svn.apache.org/viewvc/ant/core/trunk/src/main/org/apache/tools/ant/util/WorkerAnt.java?rev=589767&view=auto ============================================================================== --- ant/core/trunk/src/main/org/apache/tools/ant/util/WorkerAnt.java (added) +++ ant/core/trunk/src/main/org/apache/tools/ant/util/WorkerAnt.java Mon Oct 29 10:44:21 2007 @@ -0,0 +1,170 @@ +/* + * Copyright 2007 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.apache.tools.ant.util; + +import org.apache.tools.ant.Task; +import org.apache.tools.ant.BuildException; + +/** + * A worker ant executes a single task in a background thread. + * After the run, any exception thrown is turned into a buildexception, which can be + * rethrown, the finished attribute is set, then notifyAll() is called, + * so that anyone waiting on the same notify object gets woken up. + *

+ * This class is effectively a superset of + * {@link org.apache.tools.ant.taskdefs.Parallel.TaskRunnable} + * + * @since Ant 1.8 + */ + +public class WorkerAnt extends Thread { + + private Task task; + private Object notify; + private volatile boolean finished=false; + private volatile BuildException buildException; + private volatile Throwable exception; + + /** + * Error message if invoked with no task + */ + public static final String ERROR_NO_TASK = "No task defined"; + + + /** + * Create the worker. + *

+ * This does not start the thread, merely configures it. + * @param task the task + * @param notify what to notify + */ + public WorkerAnt(Task task, Object notify) { + this.task = task; + this.notify = notify; + } + + /** + * Create the worker, using the worker as the notification point. + *

+ * This does not start the thread, merely configures it. + * @param task the task + */ + public WorkerAnt(Task task) { + this(task,null); + notify = this; + } + + /** + * Get any build exception. + * This would seem to be oversynchronised, but know that Java pre-1.5 can reorder volatile access. + * The synchronized attribute is to force an ordering. + * + * @return the exception or null + */ + public synchronized BuildException getBuildException() { + return buildException; + } + + /** + * Get whatever was thrown, which may or may not be a buildException. + * Assertion: getException() instanceof BuildException <=> getBuildException()==getException() + * @return + */ + public synchronized Throwable getException() { + return exception; + } + + + /** + * Get the task + * @return the task + */ + public Task getTask() { + return task; + } + + + /** + * Query the task/thread for being finished. + * This would seem to be oversynchronised, but know that Java pre-1.5 can reorder volatile access. + * The synchronized attribute is to force an ordering. + * @return true if the task is finished. + */ + public synchronized boolean isFinished() { + return finished; + } + + /** + * Block on the notify object and so wait until the thread is finished. + * @param timeout timeout in milliseconds + * @throws InterruptedException if the execution was interrupted + */ + public void waitUntilFinished(long timeout) throws InterruptedException { + synchronized(notify) { + if(finished) { + return; + } + notify.wait(timeout); + } + } + + /** + * Raise an exception if one was caught + * + * @throws BuildException if one has been picked up + */ + public void rethrowAnyBuildException() { + BuildException ex = getBuildException(); + if (ex != null) { + throw ex; + } + } + + + /** + * Handle a caught exception, by recording it and possibly wrapping it + * in a BuildException for later rethrowing. + * @param thrown what was caught earlier + */ + private synchronized void caught(Throwable thrown) { + exception = thrown; + buildException = (thrown instanceof BuildException)? + (BuildException)thrown + :new BuildException(thrown); + } + + /** + * Run the task, which is skipped if null. + * When invoked again, the task is re-run. + */ + public void run() { + try { + if (task != null) { + task.execute(); + } + } catch (Throwable thrown) { + caught(thrown); + } finally { + synchronized (notify) { + finished=true; + //reset the task. + //wake up our owner, if it is waiting + notify.notifyAll(); + } + } + } +} --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscribe@ant.apache.org For additional commands, e-mail: dev-help@ant.apache.org