git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@689751 13f79535-47bb-0310-9956-ffa450edef68master
@@ -306,6 +306,12 @@ Other changes: | |||
* <echoxml> now supports XML namespaces. | |||
Bugzilla Report 36804. | |||
* A new listener for <junit> has been added that tries to invoke the | |||
tearDown method of a TestCase if that TestCase was run in a forked | |||
VM and the VM crashed or a timeout occured. See the <junit> task's | |||
manual page for details. | |||
Bugzilla Report 37241. | |||
Changes from Ant 1.7.0 TO Ant 1.7.1 | |||
============================================= | |||
@@ -569,6 +569,46 @@ supported.</p> | |||
<p>Batchtests can define their own formatters via nested | |||
<code><formatter></code> elements.</p> | |||
<h3>Forked tests and <code>tearDown</code></h3> | |||
<p>If a forked test runs into a timeout, Ant will terminate the Java | |||
VM process it has created, which probably means the | |||
test's <code>tearDown</code> method will never be called. The same | |||
is true if the forked VM crashes for some other reason.</p> | |||
<p>Starting with Ant 1.8.0, a special formatter is distributed with | |||
Ant that tries to load the testcase that was in the forked VM and | |||
invoke that class' <code>tearDown</code> method. This formatter has | |||
the following limitations:</p> | |||
<ul> | |||
<li>It runs in the same Java VM as Ant itself, this is a different | |||
Java VM than the one that was executing the test and it may see a | |||
different classloader (and thus may be unable to load the tast | |||
class).</li> | |||
<li>It cannot determine which test was run when the timeout/crash | |||
occured if the forked VM was running multiple test. I.e. the | |||
formatter cannot work with any <code>forkMode</code> other | |||
than <code>perTest</code> and it won't do anything if the test | |||
class contains a <code>suite()</code> method.</li> | |||
</ul> | |||
<p>If the formatter recognizes an incompatible <code>forkMode</code> | |||
or a <code>suite</code> method or fails to load the test class it | |||
will silently do nothing.</p> | |||
<p>The formatter doesn't have any effect on tests that were not | |||
forked or didn't cause timeouts or VM crashes.</p> | |||
<p>To enable the formatter, add a <code>formatter</code> like</p> | |||
<pre> | |||
<formatter classname="org.apache.tools.ant.taskdefs.optional.junit.TearDownOnVmCrash" | |||
usefile="false"/> | |||
</pre> | |||
<p>to your <code>junit</code> task.</p> | |||
<h3>Examples</h3> | |||
<pre> | |||
@@ -0,0 +1,50 @@ | |||
<?xml version="1.0"?> | |||
<!-- | |||
Licensed to the Apache Software Foundation (ASF) under one or more | |||
contributor license agreements. See the NOTICE file distributed with | |||
this work for additional information regarding copyright ownership. | |||
The ASF licenses this file to You 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. | |||
--> | |||
<project> | |||
<path id="test"> | |||
<pathelement path="${java.class.path}" /> | |||
<pathelement location="../../../../../../build/testcases" /> | |||
</path> | |||
<target name="testNoTeardown"> | |||
<junit haltonerror="false" errorproperty="error" fork="true" timeout="1000"> | |||
<formatter type="plain" usefile="false"/> | |||
<batchtest> | |||
<fileset dir="../../../../../../build/testcases"> | |||
<include name="org/example/junit/Timeout*"/> | |||
</fileset> | |||
</batchtest> | |||
<classpath refid="test"/> | |||
</junit> | |||
</target> | |||
<target name="testTeardown"> | |||
<junit haltonerror="false" errorproperty="error" fork="true" timeout="1000"> | |||
<formatter type="plain" usefile="false"/> | |||
<formatter classname="org.apache.tools.ant.taskdefs.optional.junit.TearDownOnVmCrash" | |||
usefile="false"/> | |||
<batchtest> | |||
<fileset dir="../../../../../../build/testcases"> | |||
<include name="org/example/junit/Timeout*"/> | |||
</fileset> | |||
</batchtest> | |||
<classpath refid="test"/> | |||
</junit> | |||
</target> | |||
</project> |
@@ -731,6 +731,7 @@ public class JUnitTask extends Task { | |||
"OutErrSummaryJUnitResultFormatter", | |||
"PlainJUnitResultFormatter", | |||
"SummaryJUnitResultFormatter", | |||
"TearDownOnVmCrash", | |||
"XMLJUnitResultFormatter", | |||
}; | |||
@@ -1502,19 +1503,19 @@ public class JUnitTask extends Task { | |||
} | |||
} | |||
static final String TIMEOUT_MESSAGE = | |||
"Timeout occurred. Please note the time in the report does" | |||
+ " not reflect the time until the timeout."; | |||
/** | |||
* Take care that some output is produced in report files if the | |||
* watchdog kills the test. | |||
* | |||
* @since Ant 1.5.2 | |||
*/ | |||
private void logTimeout(FormatterElement[] feArray, JUnitTest test, String testCase) { | |||
logVmExit( | |||
feArray, test, | |||
"Timeout occurred. Please note the time in the report does" | |||
+ " not reflect the time until the timeout.", | |||
testCase); | |||
private void logTimeout(FormatterElement[] feArray, JUnitTest test, | |||
String testCase) { | |||
logVmExit(feArray, test, TIMEOUT_MESSAGE, testCase); | |||
} | |||
/** | |||
@@ -1942,6 +1943,8 @@ public class JUnitTask extends Task { | |||
} | |||
} | |||
static final String NAME_OF_DUMMY_TEST = "Batch-With-Multiple-Tests"; | |||
/** | |||
* Creates a JUnitTest instance that shares all flags with the | |||
* passed in instance but has a more meaningful name. | |||
@@ -1962,7 +1965,7 @@ public class JUnitTask extends Task { | |||
// make sure test looks as if it was in the same "package" as | |||
// the last test of the batch | |||
String pack = index > 0 ? test.getName().substring(0, index + 1) : ""; | |||
t.setName(pack + "Batch-With-Multiple-Tests"); | |||
t.setName(pack + NAME_OF_DUMMY_TEST); | |||
return t; | |||
} | |||
@@ -0,0 +1,141 @@ | |||
/* | |||
* Licensed to the Apache Software Foundation (ASF) under one or more | |||
* contributor license agreements. See the NOTICE file distributed with | |||
* this work for additional information regarding copyright ownership. | |||
* The ASF licenses this file to You 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.junit; | |||
import java.io.OutputStream; | |||
import java.lang.reflect.InvocationTargetException; | |||
import java.lang.reflect.Method; | |||
import junit.framework.AssertionFailedError; | |||
import junit.framework.Test; | |||
/** | |||
* Formatter that doesn't create any output but tries to invoke the | |||
* tearDown method on a testcase if that test was forked and caused a | |||
* timeout or VM crash. | |||
* | |||
* <p>This formatter has some limitations, for details see the | |||
* <junit> task's manual.</p> | |||
* | |||
* @since Ant 1.8.0 | |||
*/ | |||
public class TearDownOnVmCrash implements JUnitResultFormatter { | |||
private String suiteName; | |||
/** | |||
* Records the suite's name to later determine the class to invoke | |||
* tearDown on. | |||
*/ | |||
public void startTestSuite(final JUnitTest suite) { | |||
suiteName = suite.getName(); | |||
if (suiteName != null && | |||
suiteName.endsWith(JUnitTask.NAME_OF_DUMMY_TEST)) { | |||
// no way to know which class caused the timeout | |||
suiteName = null; | |||
} | |||
} | |||
/** | |||
* Only invoke tearDown if the suite is known and not the dummy | |||
* test we get when a Batch fails and the error is an actual | |||
* error generated by Ant. | |||
*/ | |||
public void addError(final Test fakeTest, final Throwable t) { | |||
if (suiteName != null | |||
&& fakeTest instanceof JUnitTaskMirrorImpl.VmExitErrorTest) { | |||
tearDown(); | |||
} | |||
} | |||
// no need to implement the rest | |||
public void addFailure(Test test, Throwable t) {} | |||
public void addFailure(Test test, AssertionFailedError t) {} | |||
public void startTest(Test test) {} | |||
public void endTest(Test test) {} | |||
public void endTestSuite(JUnitTest suite) {} | |||
public void setOutput(OutputStream out) {} | |||
public void setSystemOutput(String out) {} | |||
public void setSystemError(String err) {} | |||
private void tearDown() { | |||
try { | |||
// first try to load the class and let's hope it is on our | |||
// classpath | |||
Class testClass = null; | |||
if (Thread.currentThread().getContextClassLoader() != null) { | |||
try { | |||
testClass = Thread.currentThread().getContextClassLoader() | |||
.loadClass(suiteName); | |||
} catch (ClassNotFoundException cnfe) { | |||
} | |||
} | |||
if (testClass == null && getClass().getClassLoader() != null) { | |||
try { | |||
testClass = | |||
getClass().getClassLoader().loadClass(suiteName); | |||
} catch (ClassNotFoundException cnfe) { | |||
} | |||
} | |||
if (testClass == null) { | |||
// fall back to system classloader | |||
testClass = Class.forName(suiteName); | |||
} | |||
// if the test has a suite method, then we can't know | |||
// which test of the executed suite timed out, ignore it | |||
try { | |||
// check if there is a suite method | |||
testClass.getMethod("suite", new Class[0]); | |||
return; | |||
} catch (NoSuchMethodException e) { | |||
// no suite method | |||
} | |||
// a loadable class and no suite method | |||
// no reason to check for JUnit 4 since JUnit4TestAdapter | |||
// doesn't have any tearDown method. | |||
try { | |||
Method td = testClass.getMethod("tearDown", new Class[0]); | |||
if (td.getReturnType() == Void.TYPE) { | |||
td.invoke(testClass.newInstance(), new Object[0]); | |||
} | |||
} catch (NoSuchMethodException nsme) { | |||
// no tearDown, fine | |||
} | |||
} catch (ClassNotFoundException cnfe) { | |||
// class probably is not in our classpath, there is | |||
// nothing we can do | |||
} catch (InvocationTargetException ite) { | |||
System.err.println("Caught an exception while trying to invoke" | |||
+ " tearDown: " + ite.getMessage()); | |||
} catch (Throwable t) { | |||
System.err.println("Caught an exception while trying to invoke" | |||
+ " tearDown: " + t.getMessage()); | |||
} | |||
} | |||
} |
@@ -0,0 +1,38 @@ | |||
/* | |||
* Licensed to the Apache Software Foundation (ASF) under one or more | |||
* contributor license agreements. See the NOTICE file distributed with | |||
* this work for additional information regarding copyright ownership. | |||
* The ASF licenses this file to You 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.junit; | |||
import org.apache.tools.ant.BuildFileTest; | |||
public class TearDownOnVmCrashTest extends BuildFileTest { | |||
public void setUp() { | |||
configureProject("src/etc/testcases/taskdefs/optional/junit/teardownlistener.xml"); | |||
} | |||
public void testNoTeardown() { | |||
expectPropertySet("testNoTeardown", "error"); | |||
assertOutputNotContaining(null, "tearDown called on Timeout"); | |||
} | |||
public void testTeardown() { | |||
expectPropertySet("testTeardown", "error"); | |||
assertOutputContaining("tearDown called on Timeout"); | |||
} | |||
} |
@@ -0,0 +1,29 @@ | |||
/* | |||
* Licensed to the Apache Software Foundation (ASF) under one or more | |||
* contributor license agreements. See the NOTICE file distributed with | |||
* this work for additional information regarding copyright ownership. | |||
* The ASF licenses this file to You 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.example.junit; | |||
import junit.framework.TestCase; | |||
public class Timeout extends TestCase { | |||
public void testTimeout() throws InterruptedException { | |||
Thread.sleep(5000); | |||
} | |||
public void tearDown() { | |||
System.out.println("tearDown called on Timeout"); | |||
} | |||
} |