Browse Source

Add a special listener that tries to invoke tearDown in a test class that caused a timeout or crashed the forked VM. PR 37241.

git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@689751 13f79535-47bb-0310-9956-ffa450edef68
master
Stefan Bodewig 17 years ago
parent
commit
b7a61200ec
7 changed files with 315 additions and 8 deletions
  1. +6
    -0
      WHATSNEW
  2. +40
    -0
      docs/manual/OptionalTasks/junit.html
  3. +50
    -0
      src/etc/testcases/taskdefs/optional/junit/teardownlistener.xml
  4. +11
    -8
      src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTask.java
  5. +141
    -0
      src/main/org/apache/tools/ant/taskdefs/optional/junit/TearDownOnVmCrash.java
  6. +38
    -0
      src/tests/junit/org/apache/tools/ant/taskdefs/optional/junit/TearDownOnVmCrashTest.java
  7. +29
    -0
      src/tests/junit/org/example/junit/Timeout.java

+ 6
- 0
WHATSNEW View File

@@ -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
=============================================



+ 40
- 0
docs/manual/OptionalTasks/junit.html View File

@@ -569,6 +569,46 @@ supported.</p>
<p>Batchtests can define their own formatters via nested
<code>&lt;formatter&gt;</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>
&lt;formatter classname="org.apache.tools.ant.taskdefs.optional.junit.TearDownOnVmCrash"
usefile="false"/&gt;
</pre>

<p>to your <code>junit</code> task.</p>

<h3>Examples</h3>

<pre>


+ 50
- 0
src/etc/testcases/taskdefs/optional/junit/teardownlistener.xml View File

@@ -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>

+ 11
- 8
src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTask.java View File

@@ -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;
}



+ 141
- 0
src/main/org/apache/tools/ant/taskdefs/optional/junit/TearDownOnVmCrash.java View File

@@ -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
* &lt;junit&gt; 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());
}
}
}

+ 38
- 0
src/tests/junit/org/apache/tools/ant/taskdefs/optional/junit/TearDownOnVmCrashTest.java View File

@@ -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");
}
}

+ 29
- 0
src/tests/junit/org/example/junit/Timeout.java View File

@@ -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");
}
}

Loading…
Cancel
Save