git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@1452674 13f79535-47bb-0310-9956-ffa450edef68master
@@ -234,6 +234,7 @@ Matthew Inger | |||
Matthew Kuperus Heun | |||
Matthew Watson | |||
Michael Bayne | |||
Michael Clarke | |||
Michael Davey | |||
Michael J. Sikorsky | |||
Michael McCallum | |||
@@ -87,6 +87,9 @@ Fixed bugs: | |||
set to ANSI_X3.4-1968. | |||
Bugzilla Report 54606 | |||
* JUnit4 tests marked @Ignore do not appear in XML output | |||
Bugzilla Report 43969 | |||
Other changes: | |||
-------------- | |||
@@ -957,6 +957,10 @@ | |||
<first>Michael</first> | |||
<last>Bayne</last> | |||
</name> | |||
<name> | |||
<first>Michael</first> | |||
<last>Clarke</last> | |||
</name> | |||
<name> | |||
<first>Michael</first> | |||
<last>Davey</last> | |||
@@ -45,7 +45,7 @@ jasper-compiler.version=4.1.36 | |||
jasper-runtime.version=${jasper-compiler.version} | |||
jdepend.version=2.9.1 | |||
jruby.version=0.9.8 | |||
junit.version=4.8.1 | |||
junit.version=4.11 | |||
jsch.version=0.1.42 | |||
jython.version=2.1 | |||
#log4j 1.2.15 requires JMS and a few other Sun jars that are not in the m2 repo | |||
@@ -17,7 +17,7 @@ | |||
# this is a first attempt to document the build of the distribution | |||
# paths are hard-coded and obviously this is for a Cygwin/Windows combo | |||
####################################################################### | |||
rm -rf bootstrap build dist distribution | |||
rm -rf bootstrap build dist distribution java-repository | |||
unset ANT_HOME | |||
# OS specific support. $var _must_ be set to either true or false. | |||
cygwin=false; | |||
@@ -276,4 +276,49 @@ | |||
</junit> | |||
</target> | |||
<!-- Junit4 Ignore and Assume for skipping tests --> | |||
<target name="testSkippableTests"> | |||
<mkdir dir="out"/> | |||
<junit fork="true"> | |||
<classpath refid="test"/> | |||
<formatter type="xml"/> | |||
<classpath refid="test"/> | |||
<batchtest todir="out"> | |||
<fileset dir="../../../../tests/junit"> | |||
<include | |||
name="org/example/junit/Junit4Skippable.java"/> | |||
<!-- tests remove out-dir on tearDown --> | |||
</fileset> | |||
</batchtest> | |||
</junit> | |||
</target> | |||
<target name="testTestMethods" > | |||
<echo file="${tmp.dir}/T1.java">public class T1 extends | |||
junit.framework.TestCase { | |||
public void testOK() {} | |||
public void testBad() {throw new RuntimeException("failed");} | |||
}</echo> | |||
<echo file="${tmp.dir}/T2.java"> | |||
import org.junit.Test; | |||
public class T2 { | |||
@Test | |||
public void ok() {} | |||
@Test | |||
public void bad() { | |||
throw new RuntimeException("failed");} | |||
}</echo> | |||
<javac srcdir="${tmp.dir}" destdir="${tmp.dir}" includes="T1.java,T2.java" source="5"> | |||
</javac> | |||
<junit fork="false" printsummary="true" haltonerror="true"> | |||
<classpath> | |||
<pathelement location="${tmp.dir}" /> | |||
<path refid="test" /> | |||
</classpath> | |||
<test name="T1" methods="testOK" /> | |||
<test name="T2" methods="ok" /> | |||
</junit> | |||
</target> | |||
</project> |
@@ -25,11 +25,13 @@ import java.io.StringWriter; | |||
import java.text.NumberFormat; | |||
import junit.framework.AssertionFailedError; | |||
import junit.framework.JUnit4TestCaseFacade; | |||
import junit.framework.Test; | |||
import org.apache.tools.ant.BuildException; | |||
import org.apache.tools.ant.util.FileUtils; | |||
import org.apache.tools.ant.util.StringUtils; | |||
import org.junit.Ignore; | |||
/** | |||
* Prints plain text output of the test to a specified Writer. | |||
@@ -38,7 +40,7 @@ import org.apache.tools.ant.util.StringUtils; | |||
* @see FormatterElement | |||
* @see PlainJUnitResultFormatter | |||
*/ | |||
public class BriefJUnitResultFormatter implements JUnitResultFormatter { | |||
public class BriefJUnitResultFormatter implements JUnitResultFormatter, IgnoredTestListener { | |||
private static final double ONE_SECOND = 1000.0; | |||
@@ -141,6 +143,8 @@ public class BriefJUnitResultFormatter implements JUnitResultFormatter { | |||
sb.append(suite.failureCount()); | |||
sb.append(", Errors: "); | |||
sb.append(suite.errorCount()); | |||
sb.append(", Skipped: "); | |||
sb.append(suite.skipCount()); | |||
sb.append(", Time elapsed: "); | |||
sb.append(numberFormat.format(suite.getRunTime() / ONE_SECOND)); | |||
sb.append(" sec"); | |||
@@ -267,4 +271,42 @@ public class BriefJUnitResultFormatter implements JUnitResultFormatter { | |||
throw new BuildException(ex); | |||
} | |||
} | |||
@Override | |||
public void testIgnored(Test test) { | |||
String message = null; | |||
if (test instanceof JUnit4TestCaseFacade) { | |||
JUnit4TestCaseFacade facade = (JUnit4TestCaseFacade) test; | |||
Ignore annotation = facade.getDescription().getAnnotation(Ignore.class); | |||
if (annotation != null && annotation.value() != null && !annotation.value().isEmpty()) { | |||
message = annotation.value(); | |||
} | |||
} | |||
formatSkip(test, message); | |||
} | |||
public void formatSkip(Test test, String message) { | |||
if (test != null) { | |||
endTest(test); | |||
} | |||
try { | |||
resultWriter.write(formatTest(test) + "SKIPPED"); | |||
if (message != null) { | |||
resultWriter.write(": "); | |||
resultWriter.write(message); | |||
} | |||
resultWriter.newLine(); | |||
} catch (IOException ex) { | |||
throw new BuildException(ex); | |||
} | |||
} | |||
@Override | |||
public void testAssumptionFailure(Test test, Throwable cause) { | |||
formatSkip(test, cause.getMessage()); | |||
} | |||
} |
@@ -0,0 +1,89 @@ | |||
/* | |||
* 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 junit.framework.JUnit4TestAdapter; | |||
import junit.framework.JUnit4TestAdapterCache; | |||
import junit.framework.TestResult; | |||
import org.junit.runner.Description; | |||
import org.junit.runner.notification.Failure; | |||
import org.junit.runner.notification.RunListener; | |||
import org.junit.runner.notification.RunNotifier; | |||
/** | |||
* Provides a custom implementation of the notifier for a Junit4TestAdapter | |||
* so that skipped and ignored tests can be reported to the existing | |||
* <tt>TestListener</tt>s. | |||
* | |||
*/ | |||
public class CustomJUnit4TestAdapterCache extends JUnit4TestAdapterCache { | |||
private static final CustomJUnit4TestAdapterCache INSTANCE = new CustomJUnit4TestAdapterCache(); | |||
public static CustomJUnit4TestAdapterCache getInstance() { | |||
return INSTANCE; | |||
} | |||
private CustomJUnit4TestAdapterCache() { | |||
super(); | |||
} | |||
public RunNotifier getNotifier(final TestResult result, final JUnit4TestAdapter adapter) { | |||
return getNotifier(result); | |||
} | |||
public RunNotifier getNotifier(final TestResult result) { | |||
final IgnoredTestResult resultWrapper = (IgnoredTestResult) result; | |||
RunNotifier notifier = new RunNotifier(); | |||
notifier.addListener(new RunListener() { | |||
@Override | |||
public void testFailure(Failure failure) throws Exception { | |||
result.addError(asTest(failure.getDescription()), failure.getException()); | |||
} | |||
@Override | |||
public void testFinished(Description description) throws Exception { | |||
result.endTest(asTest(description)); | |||
} | |||
@Override | |||
public void testStarted(Description description) throws Exception { | |||
result.startTest(asTest(description)); | |||
} | |||
@Override | |||
public void testIgnored(Description description) throws Exception { | |||
if (resultWrapper != null) { | |||
resultWrapper.testIgnored(asTest(description)); | |||
} | |||
} | |||
@Override | |||
public void testAssumptionFailure(Failure failure) { | |||
if (resultWrapper != null) { | |||
resultWrapper.testAssumptionFailure(asTest(failure.getDescription()), failure.getException()); | |||
} | |||
} | |||
}); | |||
return notifier; | |||
} | |||
} |
@@ -0,0 +1,53 @@ | |||
/* | |||
* 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 junit.framework.Test; | |||
import junit.framework.TestListener; | |||
import org.junit.runner.notification.Failure; | |||
/** | |||
* Provides the functionality for TestListeners to be able to be notified of | |||
* the necessary Junit4 events for test being ignored (@Ignore annotation) | |||
* or skipped (Assume failures). Tests written in Junit4 will report against | |||
* the methods in this interface alongside the methods in the existing TestListener | |||
*/ | |||
public interface IgnoredTestListener extends TestListener { | |||
/** | |||
* Reports when a test has been marked with the @Ignore annotation. The parameter | |||
* should normally be typed to Junit's {@link junit.framework.JUnit4TestCaseFacade} | |||
* so implementing classes should be able to get the details of the ignore by casting | |||
* the argument and retrieving the descriptor from the test. | |||
* @param test | |||
*/ | |||
void testIgnored(Test test); | |||
/** | |||
* Receive a report that a test has failed an assumption. Within JUnit4 | |||
* this is normally treated as a test being skipped, although how any | |||
* listener handles this is up to that specific listener.<br /> | |||
* <b>Note:</b> Tests that throw assumption failures will still report | |||
* the endTest method, which may differ from how the addError and addFailure | |||
* methods work, it's up for any implementing classes to handle this. | |||
* @param test the details of the test and failure that have triggered this report. | |||
* @param exception the AssumptionViolatedException thrown from the current assumption failure. | |||
*/ | |||
void testAssumptionFailure(Test test, Throwable exception); | |||
} |
@@ -0,0 +1,99 @@ | |||
/* | |||
* 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.util.ArrayList; | |||
import java.util.List; | |||
import junit.framework.Test; | |||
import junit.framework.TestListener; | |||
import junit.framework.TestResult; | |||
/** | |||
* Records ignored and skipped tests reported as part of the execution of | |||
* JUnit 4 tests. | |||
* | |||
*/ | |||
public class IgnoredTestResult extends TestResult { | |||
private List<IgnoredTestListener> listeners = new ArrayList<IgnoredTestListener>(); | |||
private List<TestIgnored> ignored = new ArrayList<TestIgnored>(); | |||
private List<TestIgnored> skipped = new ArrayList<TestIgnored>(); | |||
public IgnoredTestResult() { | |||
super(); | |||
} | |||
public synchronized void addListener(TestListener listener) { | |||
if (listener instanceof IgnoredTestListener) { | |||
listeners.add((IgnoredTestListener)listener); | |||
} | |||
super.addListener(listener); | |||
} | |||
public synchronized void removeListener(TestListener listener) { | |||
if (listener instanceof IgnoredTestListener) { | |||
listeners.remove(listener); | |||
} | |||
super.removeListener(listener); | |||
} | |||
/** | |||
* Record a test as having been ignored, normally by the @Ignore annotation. | |||
* @param test the test that was ignored. | |||
* @throws Exception is the listener thrown an exception on handling the notification. | |||
*/ | |||
public synchronized void testIgnored(Test test) throws Exception { | |||
ignored.add(new TestIgnored(test)); | |||
for (IgnoredTestListener listener : listeners) { | |||
listener.testIgnored(test); | |||
} | |||
} | |||
/** | |||
* Report how many tests were ignored. | |||
* @return the number of tests reported as ignored during the current execution. | |||
*/ | |||
public long ignoredCount() { | |||
return ignored.size(); | |||
} | |||
/** | |||
* Records a test as having an assumption failure so JUnit will no longer be executing it. | |||
* Under normal circumstances this would be counted as a skipped test. | |||
* @param test the test to record | |||
* @param cause the details of the test and assumption failure. | |||
*/ | |||
public void testAssumptionFailure(Test test, Throwable cause) { | |||
skipped.add(new TestIgnored(test)); | |||
for (IgnoredTestListener listener : listeners) { | |||
listener.testAssumptionFailure(test, cause); | |||
} | |||
} | |||
/** | |||
* Report how many tests has assumption failures. | |||
* @return the number of tests that reported assumption failures during the current execution. | |||
*/ | |||
public long skippedCount() { | |||
return skipped.size(); | |||
} | |||
} |
@@ -20,20 +20,16 @@ package org.apache.tools.ant.taskdefs.optional.junit; | |||
import java.util.Iterator; | |||
import java.util.List; | |||
import junit.framework.JUnit4TestAdapterCache; | |||
import junit.framework.Test; | |||
import junit.framework.TestResult; | |||
import org.junit.runner.Description; | |||
import org.junit.runner.Request; | |||
import org.junit.runner.Runner; | |||
import org.junit.runner.manipulation.Filter; | |||
import org.junit.runner.notification.Failure; | |||
import org.junit.runner.notification.RunListener; | |||
import org.junit.runner.notification.RunNotifier; | |||
/** | |||
* Adapter between JUnit 3.8.x API and JUnit 4.x API for execution of tests | |||
* and listening of events (test start, test finish, test failure). | |||
* and listening of events (test start, test finish, test failure, test skipped). | |||
* The constructor is passed a JUnit 4 test class and a list of name of methods | |||
* in it that should be executed. Method {@link #run run(TestResult)} executes | |||
* the given JUnit-4-style test methods and notifies the given {@code TestResult} | |||
@@ -46,7 +42,7 @@ public class JUnit4TestMethodAdapter implements Test { | |||
private final Class testClass; | |||
private final String[] methodNames; | |||
private final Runner runner; | |||
private final Cache cache; | |||
private final CustomJUnit4TestAdapterCache cache; | |||
/** | |||
* Creates a new adapter for the given class and a method within the class. | |||
@@ -75,7 +71,7 @@ public class JUnit4TestMethodAdapter implements Test { | |||
} | |||
this.testClass = testClass; | |||
this.methodNames = (String[]) methodNames.clone(); | |||
this.cache = Cache.instance; | |||
this.cache = CustomJUnit4TestAdapterCache.getInstance(); | |||
// Warning: If 'testClass' is an old-style (pre-JUnit-4) class, | |||
// then all its test methods will be executed by the returned runner! | |||
@@ -104,7 +100,7 @@ public class JUnit4TestMethodAdapter implements Test { | |||
public Class getTestClass() { | |||
return testClass; | |||
} | |||
public void run(final TestResult result) { | |||
runner.run(cache.getNotifier(result)); | |||
} | |||
@@ -188,42 +184,5 @@ public class JUnit4TestMethodAdapter implements Test { | |||
} | |||
/** | |||
* Effectively a copy of {@code JUnit4TestAdapterCache}, except that its | |||
* method {@code getNotifier()} does not require an argument | |||
* of type {@code JUnit4TestAdapter}. | |||
*/ | |||
private static final class Cache extends JUnit4TestAdapterCache { | |||
private static final long serialVersionUID = 8454901854293461610L; | |||
private static final Cache instance = new Cache(); | |||
public static JUnit4TestAdapterCache getDefault() { | |||
return instance; | |||
} | |||
public RunNotifier getNotifier(final TestResult result) { | |||
RunNotifier notifier = new RunNotifier(); | |||
notifier.addListener(new RunListener() { | |||
public void testFailure(Failure failure) throws Exception { | |||
result.addError(asTest(failure.getDescription()), | |||
failure.getException()); | |||
} | |||
public void testFinished(Description description) | |||
throws Exception { | |||
result.endTest(asTest(description)); | |||
} | |||
public void testStarted(Description description) | |||
throws Exception { | |||
result.startTest(asTest(description)); | |||
} | |||
}); | |||
return notifier; | |||
} | |||
} | |||
} |
@@ -1645,7 +1645,7 @@ public class JUnitTask extends Task { | |||
classLoader.setThreadContextLoader(); | |||
} | |||
test.setCounts(1, 0, 1); | |||
test.setCounts(1, 0, 1, 0); | |||
test.setProperties(getProject().getProperties()); | |||
for (int i = 0; i < feArray.length; i++) { | |||
FormatterElement fe = feArray[i]; | |||
@@ -62,6 +62,11 @@ public class JUnitTest extends BaseTest implements Cloneable { | |||
// part of the result. So we'd better derive a new class from TestResult | |||
// and deal with it. (SB) | |||
private long runs, failures, errors; | |||
/** | |||
@since Ant 1.9.0 | |||
*/ | |||
private long skips; | |||
private long runTime; | |||
// Snapshot of the system properties | |||
@@ -352,16 +357,31 @@ public class JUnitTest extends BaseTest implements Cloneable { | |||
} | |||
/** | |||
* Set the number of runs, failures and errors. | |||
* Set the number of runs, failures, errors, and skipped tests. | |||
* @param runs the number of runs. | |||
* @param failures the number of failures. | |||
* @param errors the number of errors. | |||
* Kept for backward compatibility with Ant 1.8.4 | |||
*/ | |||
public void setCounts(long runs, long failures, long errors) { | |||
this.runs = runs; | |||
this.failures = failures; | |||
this.errors = errors; | |||
} | |||
/** | |||
* Set the number of runs, failures, errors, and skipped tests. | |||
* @param runs the number of runs. | |||
* @param failures the number of failures. | |||
* @param errors the number of errors. | |||
* @param skips the number of skipped tests. | |||
* @since Ant 1.9.0 | |||
*/ | |||
public void setCounts(long runs, long failures, long errors, long skips) { | |||
this.runs = runs; | |||
this.failures = failures; | |||
this.errors = errors; | |||
this.skips = skips; | |||
} | |||
/** | |||
* Set the runtime. | |||
@@ -395,6 +415,14 @@ public class JUnitTest extends BaseTest implements Cloneable { | |||
return errors; | |||
} | |||
/** | |||
* Get the number of skipped tests. | |||
* @return the number of skipped tests. | |||
*/ | |||
public long skipCount() { | |||
return skips; | |||
} | |||
/** | |||
* Get the run time. | |||
* @return the run time in milliseconds. | |||
@@ -36,6 +36,7 @@ import java.util.Properties; | |||
import java.util.StringTokenizer; | |||
import java.util.Vector; | |||
import junit.framework.AssertionFailedError; | |||
import junit.framework.JUnit4TestAdapterCache; | |||
import junit.framework.Test; | |||
import junit.framework.TestFailure; | |||
import junit.framework.TestListener; | |||
@@ -76,7 +77,7 @@ public class JUnitTestRunner implements TestListener, JUnitTaskMirror.JUnitTestR | |||
/** | |||
* Collects TestResults. | |||
*/ | |||
private TestResult res; | |||
private IgnoredTestResult res; | |||
/** | |||
* Do we filter junit.*.* stack frames out of failure and error exceptions. | |||
@@ -289,6 +290,7 @@ public class JUnitTestRunner implements TestListener, JUnitTaskMirror.JUnitTestR | |||
boolean filtertrace, boolean haltOnFailure, | |||
boolean showOutput, boolean logTestListenerEvents, | |||
ClassLoader loader) { | |||
super(); | |||
JUnitTestRunner.filtertrace = filtertrace; // XXX clumsy, should use instance field somehow | |||
this.junitTest = test; | |||
this.haltOnError = haltOnError; | |||
@@ -350,7 +352,7 @@ public class JUnitTestRunner implements TestListener, JUnitTaskMirror.JUnitTestR | |||
* Run the test. | |||
*/ | |||
public void run() { | |||
res = new TestResult(); | |||
res = new IgnoredTestResult(); | |||
res.addListener(wrapListener(this)); | |||
final int size = formatters.size(); | |||
for (int i = 0; i < size; i++) { | |||
@@ -468,8 +470,8 @@ public class JUnitTestRunner implements TestListener, JUnitTaskMirror.JUnitTestR | |||
formalParams = new Class[] {Class.class, String[].class}; | |||
actualParams = new Object[] {testClass, methods}; | |||
} else { | |||
formalParams = new Class[] {Class.class}; | |||
actualParams = new Object[] {testClass}; | |||
formalParams = new Class[] {Class.class, JUnit4TestAdapterCache.class}; | |||
actualParams = new Object[] {testClass, CustomJUnit4TestAdapterCache.getInstance()}; | |||
} | |||
suite = | |||
(Test) junit4TestAdapterClass | |||
@@ -512,7 +514,7 @@ public class JUnitTestRunner implements TestListener, JUnitTaskMirror.JUnitTestR | |||
((TestListener) formatters.elementAt(i)) | |||
.addError(null, exception); | |||
} | |||
junitTest.setCounts(1, 0, 1); | |||
junitTest.setCounts(1, 0, 1, 0); | |||
junitTest.setRunTime(0); | |||
} else { | |||
try { | |||
@@ -522,10 +524,10 @@ public class JUnitTestRunner implements TestListener, JUnitTaskMirror.JUnitTestR | |||
if (junit4 || | |||
suite.getClass().getName().equals(JUNIT_4_TEST_ADAPTER)) { | |||
int[] cnts = findJUnit4FailureErrorCount(res); | |||
junitTest.setCounts(res.runCount(), cnts[0], cnts[1]); | |||
junitTest.setCounts(res.runCount() + res.ignoredCount(), cnts[0], cnts[1], res.ignoredCount() + res.skippedCount()); | |||
} else { | |||
junitTest.setCounts(res.runCount(), res.failureCount(), | |||
res.errorCount()); | |||
junitTest.setCounts(res.runCount() + res.ignoredCount(), res.failureCount(), | |||
res.errorCount(), res.ignoredCount() + res.skippedCount()); | |||
} | |||
junitTest.setRunTime(System.currentTimeMillis() - start); | |||
} | |||
@@ -1101,8 +1103,8 @@ public class JUnitTestRunner implements TestListener, JUnitTaskMirror.JUnitTestR | |||
* | |||
* @since Ant 1.7 | |||
*/ | |||
private TestListener wrapListener(final TestListener testListener) { | |||
return new TestListener() { | |||
private TestListenerWrapper wrapListener(final TestListener testListener) { | |||
return new TestListenerWrapper(testListener) { | |||
public void addError(Test test, Throwable t) { | |||
if (junit4 && t instanceof AssertionFailedError) { | |||
// JUnit 4 does not distinguish between errors and failures | |||
@@ -26,11 +26,14 @@ import java.text.NumberFormat; | |||
import java.util.Hashtable; | |||
import junit.framework.AssertionFailedError; | |||
import junit.framework.JUnit4TestCaseFacade; | |||
import junit.framework.Test; | |||
import org.apache.tools.ant.BuildException; | |||
import org.apache.tools.ant.util.FileUtils; | |||
import org.apache.tools.ant.util.StringUtils; | |||
import org.junit.Ignore; | |||
import org.junit.runner.notification.Failure; | |||
/** | |||
@@ -38,7 +41,7 @@ import org.apache.tools.ant.util.StringUtils; | |||
* | |||
*/ | |||
public class PlainJUnitResultFormatter implements JUnitResultFormatter { | |||
public class PlainJUnitResultFormatter implements JUnitResultFormatter, IgnoredTestListener { | |||
private static final double ONE_SECOND = 1000.0; | |||
@@ -123,6 +126,8 @@ public class PlainJUnitResultFormatter implements JUnitResultFormatter { | |||
sb.append(suite.failureCount()); | |||
sb.append(", Errors: "); | |||
sb.append(suite.errorCount()); | |||
sb.append(", Skipped: "); | |||
sb.append(suite.skipCount()); | |||
sb.append(", Time elapsed: "); | |||
sb.append(nf.format(suite.getRunTime() / ONE_SECOND)); | |||
sb.append(" sec"); | |||
@@ -258,4 +263,40 @@ public class PlainJUnitResultFormatter implements JUnitResultFormatter { | |||
} | |||
} | |||
@Override | |||
public void testIgnored(Test test) { | |||
String message = null; | |||
if (test instanceof JUnit4TestCaseFacade) { | |||
JUnit4TestCaseFacade facade = (JUnit4TestCaseFacade) test; | |||
Ignore annotation = facade.getDescription().getAnnotation(Ignore.class); | |||
if (annotation != null && annotation.value() != null && !annotation.value().isEmpty()) { | |||
message = annotation.value(); | |||
} | |||
} | |||
formatSkip(test, message); | |||
} | |||
public void formatSkip(Test test, String message) { | |||
if (test != null) { | |||
endTest(test); | |||
} | |||
try { | |||
wri.write("\tSKIPPED"); | |||
if (message != null) { | |||
wri.write(": "); | |||
wri.write(message); | |||
} | |||
wri.newLine(); | |||
} catch (IOException ex) { | |||
throw new BuildException(ex); | |||
} | |||
} | |||
@Override | |||
public void testAssumptionFailure(Test test, Throwable throwable) { | |||
formatSkip(test, throwable.getMessage()); | |||
} | |||
} // PlainJUnitResultFormatter |
@@ -144,6 +144,8 @@ public class SummaryJUnitResultFormatter | |||
sb.append(suite.failureCount()); | |||
sb.append(", Errors: "); | |||
sb.append(suite.errorCount()); | |||
sb.append(", Skipped: "); | |||
sb.append(suite.skipCount()); | |||
sb.append(", Time elapsed: "); | |||
sb.append(nf.format(suite.getRunTime() / ONE_SECOND)); | |||
sb.append(" sec"); | |||
@@ -0,0 +1,35 @@ | |||
/* | |||
* 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 junit.framework.Test; | |||
public class TestIgnored { | |||
private Test test; | |||
public TestIgnored(Test test) { | |||
this.test = test; | |||
} | |||
public Test getTest() { | |||
return test; | |||
} | |||
} |
@@ -0,0 +1,74 @@ | |||
/* | |||
* 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 junit.framework.AssertionFailedError; | |||
import junit.framework.JUnit4TestAdapterCache; | |||
import junit.framework.Test; | |||
import junit.framework.TestListener; | |||
import org.junit.internal.AssumptionViolatedException; | |||
import org.junit.runner.Description; | |||
import org.junit.runner.notification.Failure; | |||
public class TestListenerWrapper implements TestListener, IgnoredTestListener { | |||
private TestListener wrapped; | |||
public TestListenerWrapper(TestListener listener) { | |||
super(); | |||
wrapped = listener; | |||
} | |||
@Override | |||
public void addError(Test test, Throwable throwable) { | |||
wrapped.addError(test, throwable); | |||
} | |||
@Override | |||
public void addFailure(Test test, AssertionFailedError assertionFailedError) { | |||
wrapped.addFailure(test, assertionFailedError); | |||
} | |||
@Override | |||
public void endTest(Test test) { | |||
wrapped.endTest(test); | |||
} | |||
@Override | |||
public void startTest(Test test) { | |||
wrapped.startTest(test); | |||
} | |||
@Override | |||
public void testIgnored(Test test) { | |||
if (wrapped instanceof IgnoredTestListener) { | |||
((IgnoredTestListener)wrapped).testIgnored(test); | |||
} | |||
} | |||
@Override | |||
public void testAssumptionFailure(Test test, Throwable throwable) { | |||
if (wrapped instanceof IgnoredTestListener) { | |||
((IgnoredTestListener)wrapped).testAssumptionFailure(test, throwable); | |||
} | |||
} | |||
} |
@@ -106,6 +106,8 @@ public interface XMLConstants { | |||
/** tests attribute for testsuite elements */ | |||
String ATTR_TESTS = "tests"; | |||
String ATTR_SKIPPED = "skipped"; | |||
/** type attribute for failure and error elements */ | |||
String ATTR_TYPE = "type"; | |||
@@ -23,20 +23,26 @@ import java.io.IOException; | |||
import java.io.OutputStream; | |||
import java.io.OutputStreamWriter; | |||
import java.io.Writer; | |||
import java.lang.reflect.Method; | |||
import java.net.InetAddress; | |||
import java.net.UnknownHostException; | |||
import java.util.Date; | |||
import java.util.Enumeration; | |||
import java.util.Hashtable; | |||
import java.util.Properties; | |||
import java.util.Date; | |||
import java.net.InetAddress; | |||
import java.net.UnknownHostException; | |||
import javax.xml.parsers.DocumentBuilder; | |||
import javax.xml.parsers.DocumentBuilderFactory; | |||
import junit.framework.AssertionFailedError; | |||
import junit.framework.JUnit4TestCaseFacade; | |||
import junit.framework.Test; | |||
import org.apache.tools.ant.BuildException; | |||
import org.apache.tools.ant.util.DOMElementWriter; | |||
import org.apache.tools.ant.util.DateUtils; | |||
import org.apache.tools.ant.util.FileUtils; | |||
import org.junit.Ignore; | |||
import org.w3c.dom.Document; | |||
import org.w3c.dom.Element; | |||
import org.w3c.dom.Text; | |||
@@ -48,7 +54,7 @@ import org.w3c.dom.Text; | |||
* @see FormatterElement | |||
*/ | |||
public class XMLJUnitResultFormatter implements JUnitResultFormatter, XMLConstants { | |||
public class XMLJUnitResultFormatter implements JUnitResultFormatter, XMLConstants, IgnoredTestListener { | |||
private static final double ONE_SECOND = 1000.0; | |||
@@ -73,16 +79,29 @@ public class XMLJUnitResultFormatter implements JUnitResultFormatter, XMLConstan | |||
private Element rootElement; | |||
/** | |||
* Element for the current test. | |||
* | |||
* The keying of this map is a bit of a hack: tests are keyed by caseName(className) since | |||
* the Test we get for Test-start isn't the same as the Test we get during test-assumption-fail, | |||
* so we can't easily match Test objects without manually iterating over all keys and checking | |||
* individual fields. | |||
*/ | |||
private Hashtable testElements = new Hashtable(); | |||
private Hashtable<String, Element> testElements = new Hashtable<String, Element>(); | |||
/** | |||
* tests that failed. | |||
*/ | |||
private Hashtable failedTests = new Hashtable(); | |||
/** | |||
* Tests that were skipped. | |||
*/ | |||
private Hashtable<String, Test> skippedTests = new Hashtable<String, Test>(); | |||
/** | |||
* Tests that were ignored. See the note above about the key being a bit of a hack. | |||
*/ | |||
private Hashtable<String, Test> ignoredTests = new Hashtable<String, Test>(); | |||
/** | |||
* Timing helper. | |||
*/ | |||
private Hashtable testStarts = new Hashtable(); | |||
private Hashtable<String, Long> testStarts = new Hashtable<String, Long>(); | |||
/** | |||
* Where to write the log to. | |||
*/ | |||
@@ -161,6 +180,7 @@ public class XMLJUnitResultFormatter implements JUnitResultFormatter, XMLConstan | |||
rootElement.setAttribute(ATTR_TESTS, "" + suite.runCount()); | |||
rootElement.setAttribute(ATTR_FAILURES, "" + suite.failureCount()); | |||
rootElement.setAttribute(ATTR_ERRORS, "" + suite.errorCount()); | |||
rootElement.setAttribute(ATTR_SKIPPED, "" + suite.skipCount()); | |||
rootElement.setAttribute( | |||
ATTR_TIME, "" + (suite.getRunTime() / ONE_SECOND)); | |||
if (out != null) { | |||
@@ -193,9 +213,13 @@ public class XMLJUnitResultFormatter implements JUnitResultFormatter, XMLConstan | |||
* @param t the test. | |||
*/ | |||
public void startTest(Test t) { | |||
testStarts.put(t, new Long(System.currentTimeMillis())); | |||
testStarts.put(createDescription(t), System.currentTimeMillis()); | |||
} | |||
private static String createDescription(Test test) throws BuildException { | |||
return JUnitVersionHelper.getTestCaseName(test) + "(" + JUnitVersionHelper.getTestCaseClassName(test) + ")"; | |||
} | |||
/** | |||
* Interface TestListener. | |||
* | |||
@@ -203,15 +227,16 @@ public class XMLJUnitResultFormatter implements JUnitResultFormatter, XMLConstan | |||
* @param test the test. | |||
*/ | |||
public void endTest(Test test) { | |||
String testDescription = createDescription(test); | |||
// Fix for bug #5637 - if a junit.extensions.TestSetup is | |||
// used and throws an exception during setUp then startTest | |||
// would never have been called | |||
if (!testStarts.containsKey(test)) { | |||
if (!testStarts.containsKey(testDescription)) { | |||
startTest(test); | |||
} | |||
Element currentTest = null; | |||
if (!failedTests.containsKey(test)) { | |||
Element currentTest; | |||
if (!failedTests.containsKey(test) && !skippedTests.containsKey(testDescription) && !ignoredTests.containsKey(testDescription)) { | |||
currentTest = doc.createElement(TESTCASE); | |||
String n = JUnitVersionHelper.getTestCaseName(test); | |||
currentTest.setAttribute(ATTR_NAME, | |||
@@ -221,15 +246,14 @@ public class XMLJUnitResultFormatter implements JUnitResultFormatter, XMLConstan | |||
currentTest.setAttribute(ATTR_CLASSNAME, | |||
JUnitVersionHelper.getTestCaseClassName(test)); | |||
rootElement.appendChild(currentTest); | |||
testElements.put(test, currentTest); | |||
testElements.put(createDescription(test), currentTest); | |||
} else { | |||
currentTest = (Element) testElements.get(test); | |||
currentTest = testElements.get(testDescription); | |||
} | |||
Long l = (Long) testStarts.get(test); | |||
Long l = testStarts.get(createDescription(test)); | |||
currentTest.setAttribute(ATTR_TIME, | |||
"" + ((System.currentTimeMillis() | |||
- l.longValue()) / ONE_SECOND)); | |||
"" + ((System.currentTimeMillis() - l) / ONE_SECOND)); | |||
} | |||
/** | |||
@@ -272,9 +296,9 @@ public class XMLJUnitResultFormatter implements JUnitResultFormatter, XMLConstan | |||
} | |||
Element nested = doc.createElement(type); | |||
Element currentTest = null; | |||
Element currentTest; | |||
if (test != null) { | |||
currentTest = (Element) testElements.get(test); | |||
currentTest = testElements.get(createDescription(test)); | |||
} else { | |||
currentTest = rootElement; | |||
} | |||
@@ -298,4 +322,63 @@ public class XMLJUnitResultFormatter implements JUnitResultFormatter, XMLConstan | |||
nested.appendChild(doc.createCDATASection(output)); | |||
} | |||
@Override | |||
public void testIgnored(Test test) { | |||
String message = null; | |||
if (test != null && test instanceof JUnit4TestCaseFacade) { | |||
//try and get the message coded as part of the ignore | |||
/* | |||
* org.junit.runner.Description contains a getAnnotation(Class) method... but this | |||
* wasn't in older versions of JUnit4 so we have to try and do this by reflection | |||
*/ | |||
try { | |||
Class<?> testClass = Class.forName(JUnitVersionHelper.getTestCaseClassName(test)); | |||
Method testMethod = testClass.getMethod(JUnitVersionHelper.getTestCaseName(test)); | |||
Ignore annotation = testMethod.getAnnotation(Ignore.class); | |||
if (annotation != null && annotation.value() != null && !annotation.value().isEmpty()) { | |||
message = annotation.value(); | |||
} | |||
} catch (NoSuchMethodException e) { | |||
// silently ignore - we'll report a skip with no message | |||
} catch (ClassNotFoundException e) { | |||
// silently ignore - we'll report a skip with no message | |||
} | |||
} | |||
formatSkip(test, message); | |||
if (test != null) { | |||
ignoredTests.put(createDescription(test), test); | |||
} | |||
} | |||
public void formatSkip(Test test, String message) { | |||
if (test != null) { | |||
endTest(test); | |||
} | |||
Element nested = doc.createElement("skipped"); | |||
if (message != null) { | |||
nested.setAttribute("message", message); | |||
} | |||
Element currentTest; | |||
if (test != null) { | |||
currentTest = testElements.get(createDescription(test)); | |||
} else { | |||
currentTest = rootElement; | |||
} | |||
currentTest.appendChild(nested); | |||
} | |||
@Override | |||
public void testAssumptionFailure(Test test, Throwable failure) { | |||
String message = failure.getMessage(); | |||
formatSkip(test, message); | |||
skippedTests.put(createDescription(test), test); | |||
} | |||
} // XMLJUnitResultFormatter |
@@ -20,6 +20,7 @@ | |||
<path id="junit"> | |||
<fileset dir="../../../../../../lib/optional" includes="junit*" /> | |||
<fileset dir="../../../../../../lib/optional" includes="hamcrest-core*" /> | |||
</path> | |||
<macrodef name="empty-test"> | |||
@@ -25,6 +25,14 @@ import java.io.IOException; | |||
import org.apache.tools.ant.BuildException; | |||
import org.apache.tools.ant.BuildFileTest; | |||
import org.apache.tools.ant.util.JavaEnvUtils; | |||
import org.w3c.dom.Document; | |||
import org.w3c.dom.Node; | |||
import javax.xml.parsers.DocumentBuilder; | |||
import javax.xml.parsers.DocumentBuilderFactory; | |||
import javax.xml.xpath.XPath; | |||
import javax.xml.xpath.XPathConstants; | |||
import javax.xml.xpath.XPathFactory; | |||
public class JUnitTaskTest extends BuildFileTest { | |||
@@ -262,7 +270,7 @@ public class JUnitTaskTest extends BuildFileTest { | |||
line); | |||
line = reader.readLine(); | |||
assertNotNull(line); | |||
assertTrue(line.startsWith("Tests run: 1, Failures: 0, Errors: 0, Time elapsed:")); | |||
assertTrue(line.startsWith("Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed:")); | |||
line = reader.readLine(); | |||
assertEquals("------------- Standard Output ---------------", | |||
line); | |||
@@ -296,4 +304,33 @@ public class JUnitTaskTest extends BuildFileTest { | |||
assertEquals(search, line); | |||
} | |||
public void testJunit4Skip() throws Exception { | |||
executeTarget("testSkippableTests"); | |||
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance(); | |||
DocumentBuilder dBuilder = dbFactory.newDocumentBuilder(); | |||
Document doc = dBuilder.parse(getProject().getResource("out/TEST-org.example.junit.Junit4Skippable.xml").getInputStream()); | |||
assertEquals("Incorrect number of nodes created", 8, doc.getElementsByTagName("testcase").getLength()); | |||
XPathFactory factory = XPathFactory.newInstance(); | |||
XPath xpath = factory.newXPath(); | |||
assertEquals("Incorrect number of skipped tests in header", 4, Integer.parseInt(xpath.compile("//testsuite/@skipped").evaluate(doc))); | |||
assertEquals("Incorrect number of error tests in header", 1, Integer.parseInt(xpath.compile("//testsuite/@errors").evaluate(doc))); | |||
assertEquals("Incorrect number of failure tests in header", 2, Integer.parseInt(xpath.compile("//testsuite/@failures").evaluate(doc))); | |||
assertEquals("Incorrect number of tests in header", 8, Integer.parseInt(xpath.compile("//testsuite/@tests").evaluate(doc))); | |||
assertEquals("Incorrect ignore message on explicit ignored test", "Please don't ignore me!", xpath.compile("//testsuite/testcase[@name='explicitIgnoreTest']/skipped/@message").evaluate(doc)); | |||
assertEquals("No message should be set on Ignored tests with no Ignore annotation text", 0, ((Node)xpath.compile("//testsuite/testcase[@name='explicitlyIgnoreTestNoMessage']/skipped").evaluate(doc, XPathConstants.NODE)).getAttributes().getLength()); | |||
assertEquals("Incorrect ignore message on implicit ignored test", "This test will be ignored", xpath.compile("//testsuite/testcase[@name='implicitlyIgnoreTest']/skipped/@message").evaluate(doc)); | |||
assertNotNull("Implicit ignore test should have an ignore element", xpath.compile("//testsuite/testcase[@name='implicitlyIgnoreTestNoMessage']/skipped").evaluate(doc, XPathConstants.NODE)); | |||
} | |||
public void testTestMethods() throws Exception { | |||
executeTarget("testTestMethods"); | |||
} | |||
} |
@@ -0,0 +1,56 @@ | |||
package org.example.junit; | |||
import org.junit.Assume; | |||
import org.junit.Ignore; | |||
import org.junit.Test; | |||
import static org.junit.Assert.assertTrue; | |||
import static org.junit.Assert.fail; | |||
public class Junit4Skippable { | |||
@Test | |||
public void passingTest() { | |||
assertTrue("This test passed", true); | |||
} | |||
@Ignore("Please don't ignore me!") | |||
@Test | |||
public void explicitIgnoreTest() { | |||
fail("This test should be skipped"); | |||
} | |||
@Test | |||
public void implicitlyIgnoreTest() { | |||
Assume.assumeFalse("This test will be ignored", true); | |||
fail("I told you, this test should have been ignored!"); | |||
} | |||
@Test | |||
@Ignore | |||
public void explicitlyIgnoreTestNoMessage() { | |||
fail("This test should be skipped"); | |||
} | |||
@Test | |||
public void implicitlyIgnoreTestNoMessage() { | |||
Assume.assumeFalse(true); | |||
fail("I told you, this test should have been ignored!"); | |||
} | |||
@Test | |||
public void failingTest() { | |||
fail("I told you this test was going to fail"); | |||
} | |||
@Test | |||
public void failingTestNoMessage() { | |||
fail(); | |||
} | |||
@Test | |||
public void errorTest() { | |||
throw new RuntimeException("Whoops, this test went wrong"); | |||
} | |||
} |