Browse Source

#34748: run individual test methods.

git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@953761 13f79535-47bb-0310-9956-ffa450edef68
master
Jesse N. Glick 15 years ago
parent
commit
114d55869f
16 changed files with 850 additions and 21 deletions
  1. +3
    -0
      WHATSNEW
  2. +1
    -1
      build.xml
  3. +20
    -0
      docs/manual/Tasks/junit.html
  4. +1
    -1
      lib/libraries.properties
  5. +1
    -1
      lib/optional/README
  6. BIN
      lib/optional/junit-3.8.2.jar
  7. BIN
      lib/optional/junit-4.8.1.jar
  8. +1
    -0
      src/main/org/apache/tools/ant/taskdefs/optional/junit/Constants.java
  9. +227
    -0
      src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnit4TestMethodAdapter.java
  10. +33
    -1
      src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTask.java
  11. +2
    -1
      src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTaskMirror.java
  12. +2
    -1
      src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTaskMirrorImpl.java
  13. +240
    -1
      src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTest.java
  14. +132
    -11
      src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTestRunner.java
  15. +142
    -0
      src/tests/junit/org/apache/tools/ant/taskdefs/optional/junit/BatchTestTest.java
  16. +45
    -3
      src/tests/junit/org/apache/tools/ant/taskdefs/optional/junit/JUnitTestRunnerTest.java

+ 3
- 0
WHATSNEW View File

@@ -61,6 +61,9 @@ Other changes:
overwrite attribute that is consistent with <copy>'s attribute
names.

* You can now specify a list of methods to run in a JUnit test case.
Bugzilla Report 34748.

Changes from Ant 1.8.0 TO Ant 1.8.1
===================================



+ 1
- 1
build.xml View File

@@ -350,7 +350,7 @@
classname="org.apache.xalan.trace.TraceListenerEx3"
classpathref="classpath" ignoresystemclasses="true"/>
<available property="junit.present"
classname="junit.framework.TestCase"
classname="org.junit.Test"
classpathref="classpath" ignoresystemclasses="true"/>
<available property="antunit.present"
classname="org.apache.ant.antunit.AntUnit"


+ 20
- 0
docs/manual/Tasks/junit.html View File

@@ -430,6 +430,26 @@ the name of the resulting class (without suffix). It defaults to <i>java-tmp-dir
<td valign="top">Name of the test class.</td>
<td align="center">Yes</td>
</tr>
<tr>
<td valign="top">methods</td>
<td valign="top">Comma-separated list of names of test case methods to execute.
<em>Since 1.8.2</em>
<p>The <code>methods</code> attribute can be useful in the following scenarios:</p>
<ul>
<li>A test method has failed and you want to re-run the test method
to test a fix or re-run the test under the Java debugger without
having to wait for the other (possibly long running) test methods
to complete.</li>
<li>One or more test methods are running slower than expected and you
want to re-run them under a Java profiler (without the overhead
of running the profiler whilst other test methods are being
executed).</li>
</ul>
<p>If the <code>methods</code> attribute is used but no test method
is specified, then no test method from the suite will be executed.</p>
</td>
<td align="center">No; default is to run all test methods in the suite.</td>
</tr>
<tr>
<td valign="top">fork</td>
<td valign="top">Run the tests in a separate VM.


+ 1
- 1
lib/libraries.properties View File

@@ -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=3.8.2
junit.version=4.8.1
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


+ 1
- 1
lib/optional/README View File

@@ -1,3 +1,3 @@
The file junit-3.8.2.jar is version 3.8.2 of JUnit, see the file LICENSE.junit.html
The file junit-4.8.1.jar is version 4.8.1 of JUnit, see the file LICENSE.junit.html
for the terms of distribution. For more information about JUnit or
the latest release, see <http://www.junit.org/>.

BIN
lib/optional/junit-3.8.2.jar View File


BIN
lib/optional/junit-4.8.1.jar View File


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

@@ -23,6 +23,7 @@ package org.apache.tools.ant.taskdefs.optional.junit;
*/
public class Constants {

static final String METHOD_NAMES = "methods=";
static final String HALT_ON_ERROR = "haltOnError=";
static final String HALT_ON_FAILURE = "haltOnFailure=";
static final String FILTERTRACE = "filtertrace=";


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

@@ -0,0 +1,227 @@
/*
* 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.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).
* 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}
* object using its old (JUnit 3.8.x style) API.
*
* @author Marian Petras
*/
public class JUnit4TestMethodAdapter implements Test {

private final Class testClass;
private final String[] methodNames;
private final Runner runner;
private final Cache cache;

/**
* Creates a new adapter for the given class and a method within the class.
*
* @param testClass test class containing the method to be executed
* @param methodNames names of the test methods that are to be executed
* @exception java.lang.IllegalArgumentException
* if any of the arguments is {@code null}
* or if any of the given method names is {@code null} or empty
*/
public JUnit4TestMethodAdapter(final Class testClass,
final String[] methodNames) {
if (testClass == null) {
throw new IllegalArgumentException("testClass is <null>");
}
if (methodNames == null) {
throw new IllegalArgumentException("methodNames is <null>");
}
for (int i = 0; i < methodNames.length; i++) {
if (methodNames[i] == null) {
throw new IllegalArgumentException("method name #" + i + " is <null>");
}
if (methodNames[i].length() == 0) {
throw new IllegalArgumentException("method name #" + i + " is empty");
}
}
this.testClass = testClass;
this.methodNames = methodNames;
this.cache = Cache.instance;

// Warning: If 'testClass' is an old-style (pre-JUnit-4) class,
// then all its test methods will be executed by the returned runner!
Request request;
if (methodNames.length == 1) {
request = Request.method(testClass, methodNames[0]);
} else {
request = Request.aClass(testClass).filterWith(
new MultipleMethodsFilter(testClass, methodNames));
}
runner = request.getRunner();
}

public int countTestCases() {
return runner.testCount();
}

public Description getDescription() {
return runner.getDescription();
}

public List/*<Test>*/ getTests() {
return cache.asTestList(getDescription());
}

public Class getTestClass() {
return testClass;
}
public void run(final TestResult result) {
runner.run(cache.getNotifier(result));
}

public String toString() {
String testClassName = testClass.getName();
StringBuilder buf = new StringBuilder(testClassName.length()
+ 12 * methodNames.length)
.append(':');
if (methodNames.length != 0) {
buf.append(methodNames[0]);
for (int i = 1; i < methodNames.length; i++) {
buf.append(',')
.append(methodNames[i]);
}
}
return buf.toString();
}

private static final class MultipleMethodsFilter extends Filter {

private final Description methodsListDescription;
private final Class testClass;
private final String[] methodNames;

private MultipleMethodsFilter(Class testClass, String[] methodNames) {
if (testClass == null) {
throw new IllegalArgumentException("testClass is <null>");
}
if (methodNames == null) {
throw new IllegalArgumentException("methodNames is <null>");
}
methodsListDescription = Description.createSuiteDescription(testClass);
for (int i = 0; i < methodNames.length; i++) {
methodsListDescription.addChild(
Description.createTestDescription(testClass, methodNames[i]));
}
this.testClass = testClass;
this.methodNames = methodNames;
}

public boolean shouldRun(Description description) {
if (methodNames.length == 0) {
return false;
}
if (description.isTest()) {
Iterator/*<Description>*/ it = methodsListDescription.getChildren().iterator();
while (it.hasNext()) {
Description methodDescription = (Description) it.next();
if (methodDescription.equals(description)) {
return true;
}
}
} else {
Iterator/*<Description>*/ it = description.getChildren().iterator();
while (it.hasNext()) {
Description each = (Description) it.next();
if (shouldRun(each)) {
return true;
}
}
}
return false;
}

public String describe() {
StringBuilder buf = new StringBuilder(40);
if (methodNames.length == 0) {
buf.append("No methods");
} else {
buf.append(methodNames.length == 1 ? "Method" : "Methods");
buf.append(' ');
buf.append(methodNames[0]);
for (int i = 1; i < methodNames.length; i++) {
buf.append(',').append(methodNames[i]);
}
}
buf.append('(').append(testClass.getName()).append(')');
return buf.toString();
}

}

/**
* 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 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;
}

}

}

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

@@ -728,6 +728,7 @@ public class JUnitTask extends Task {
new SplitClassLoader(myLoader, path, getProject(),
new String[] {
"BriefJUnitResultFormatter",
"JUnit4TestMethodAdapter",
"JUnitResultFormatter",
"JUnitTaskMirrorImpl",
"JUnitTestRunner",
@@ -751,6 +752,8 @@ public class JUnitTask extends Task {
* @since Ant 1.2
*/
public void execute() throws BuildException {
checkMethodLists();

setupJUnitDelegate();

List testLists = new ArrayList();
@@ -851,6 +854,9 @@ public class JUnitTask extends Task {
while (iter.hasNext()) {
test = (JUnitTest) iter.next();
printDual(writer, logWriter, test.getName());
if (test.getMethods() != null) {
printDual(writer, logWriter, ":" + test.getMethodsString().replace(',', '+'));
}
if (test.getTodir() == null) {
printDual(writer, logWriter,
"," + getProject().resolveFile("."));
@@ -922,6 +928,9 @@ public class JUnitTask extends Task {
cmd.setClassname("org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner");
if (casesFile == null) {
cmd.createArgument().setValue(test.getName());
if (test.getMethods() != null) {
cmd.createArgument().setValue(Constants.METHOD_NAMES + test.getMethodsString());
}
} else {
log("Running multiple tests in the same VM", Project.MSG_VERBOSE);
cmd.createArgument().setValue(Constants.TESTSFILE + casesFile);
@@ -1322,7 +1331,7 @@ public class JUnitTask extends Task {
if (classLoader != null) {
classLoader.setThreadContextLoader();
}
runner = delegate.newJUnitTestRunner(test, test.getHaltonerror(),
runner = delegate.newJUnitTestRunner(test, test.getMethods(), test.getHaltonerror(),
test.getFiltertrace(),
test.getHaltonfailure(), false,
true, classLoader);
@@ -1407,6 +1416,29 @@ public class JUnitTask extends Task {
return Enumerations.fromCompound(enums);
}

/**
* Verifies all <code>test</code> elements having the <code>methods</code>
* attribute specified and having the <code>if</code>-condition resolved
* to true, that the value of the <code>methods</code> attribute is valid.
* @exception BuildException if some of the tests matching the described
* conditions has invalid value of the
* <code>methods</code> attribute
* @since 1.8.2
*/
private void checkMethodLists() throws BuildException {
if (tests.isEmpty()) {
return;
}

Enumeration testsEnum = tests.elements();
while (testsEnum.hasMoreElements()) {
JUnitTest test = (JUnitTest) testsEnum.nextElement();
if (test.hasMethodsSpecified() && test.shouldRun(getProject())) {
test.resolveMethods();
}
}
}

/**
* return an enumeration listing each test, then each batchtest
* @return enumeration


+ 2
- 1
src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTaskMirror.java View File

@@ -55,6 +55,7 @@ public interface JUnitTaskMirror {
/**
* Create a new test runner for a test.
* @param test the test to run.
* @param methods names of the test methods to be run.
* @param haltOnError if true halt the tests if an error occurs.
* @param filterTrace if true filter the stack traces.
* @param haltOnFailure if true halt the test if a failure occurs.
@@ -63,7 +64,7 @@ public interface JUnitTaskMirror {
* @param classLoader the classloader to use to create the runner.
* @return the test runner.
*/
JUnitTestRunnerMirror newJUnitTestRunner(JUnitTest test, boolean haltOnError,
JUnitTestRunnerMirror newJUnitTestRunner(JUnitTest test, String[] methods, boolean haltOnError,
boolean filterTrace, boolean haltOnFailure, boolean showOutput,
boolean logTestListenerEvents, AntClassLoader classLoader);



+ 2
- 1
src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTaskMirrorImpl.java View File

@@ -60,9 +60,10 @@ public final class JUnitTaskMirrorImpl implements JUnitTaskMirror {

/** {@inheritDoc}. */
public JUnitTaskMirror.JUnitTestRunnerMirror newJUnitTestRunner(JUnitTest test,
String[] methods,
boolean haltOnError, boolean filterTrace, boolean haltOnFailure,
boolean showOutput, boolean logTestListenerEvents, AntClassLoader classLoader) {
return new JUnitTestRunner(test, haltOnError, filterTrace, haltOnFailure,
return new JUnitTestRunner(test, methods, haltOnError, filterTrace, haltOnFailure,
showOutput, logTestListenerEvents, classLoader);
}



+ 240
- 1
src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTest.java View File

@@ -22,6 +22,7 @@ import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Properties;
import java.util.Vector;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.PropertyHelper;

@@ -41,6 +42,19 @@ public class JUnitTest extends BaseTest implements Cloneable {
/** the name of the test case */
private String name = null;

/**
* whether the list of test methods has been specified
* @see #setMethods(java.lang.String)
* @see #setMethods(java.lang.String[])
*/
private boolean methodsSpecified = false;

/** comma-separated list of names of test methods to execute */
private String methodsList = null;

/** the names of test methods to execute */
private String[] methods = null;
/** the name of the result file */
private String outfile = null;

@@ -73,11 +87,53 @@ public class JUnitTest extends BaseTest implements Cloneable {
* @param filtertrace if true filter stack traces.
*/
public JUnitTest(String name, boolean haltOnError, boolean haltOnFailure,
boolean filtertrace) {
boolean filtertrace) {
this(name, haltOnError, haltOnFailure, filtertrace, null);
}
/**
* Constructor with options.
* @param name the name of the test.
* @param haltOnError if true halt the tests if there is an error.
* @param haltOnFailure if true halt the tests if there is a failure.
* @param filtertrace if true filter stack traces.
* @param methods if true run only test methods that failed during the
* previous run of the test suite
* @since 1.8.2
*/
public JUnitTest(String name, boolean haltOnError, boolean haltOnFailure,
boolean filtertrace, String[] methods) {
this.name = name;
this.haltOnError = haltOnError;
this.haltOnFail = haltOnFailure;
this.filtertrace = filtertrace;
this.methods = methods;
this.methodsSpecified = (methods != null);
}

/**
* Sets names of individual test methods to be executed.
* @param value comma-separated list of names of individual test methods
* to be executed,
* or <code>null</code> if all test methods should be executed
* @since 1.8.2
*/
public void setMethods(String value) {
methodsList = value;
methodsSpecified = (value != null);
methods = null;
}

/**
* Sets names of individual test methods to be executed.
* @param value non-empty array of names of test methods to be executed
* @see #setMethods(String)
* @since 1.8.2
*/
void setMethods(String[] value) {
methods = value;
methodsSpecified = (value != null);
methodsList = null;
}

/**
@@ -96,6 +152,189 @@ public class JUnitTest extends BaseTest implements Cloneable {
outfile = value;
}

/**
* Informs whether a list of test methods has been specified in this test.
* @return <code>true</code> if test methods to be executed have been
* specified, <code>false</code> otherwise
* @see #setMethods(java.lang.String)
* @see #setMethods(java.lang.String[])
* @since 1.8.2
*/
boolean hasMethodsSpecified() {
return methodsSpecified;
}

/**
* Get names of individual test methods to be executed.
*
* @return array of names of the individual test methods to be executed,
* or <code>null</code> if all test methods in the suite
* defined by the test class will be executed
* @since 1.8.2
*/
String[] getMethods() {
if (methodsSpecified && (methods == null)) {
resolveMethods();
}
return methods;
}

/**
* Gets a comma-separated list of names of methods that are to be executed
* by this test.
* @return the comma-separated list of test method names, or an empty
* string of no method is to be executed, or <code>null</code>
* if no method is specified
* @since 1.8.2
*/
String getMethodsString() {
if ((methodsList == null) && methodsSpecified) {
if (methods.length == 0) {
methodsList = "";
} else if (methods.length == 1) {
methodsList = methods[0];
} else {
StringBuffer buf = new StringBuffer(methods.length * 16);
buf.append(methods[0]);
for (int i = 1; i < methods.length; i++) {
buf.append(',').append(methods[i]);
}
methodsList = buf.toString();
}
}
return methodsList;
}

/**
* Computes the value of the {@link #methods} field from the value
* of the {@link #methodsList} field, if it has not been computed yet.
* @exception BuildException if the value of the {@link #methodsList} field
* was invalid
* @since 1.8.2
*/
void resolveMethods() {
if ((methods == null) && methodsSpecified) {
try {
methods = parseTestMethodNamesList(methodsList);
} catch (IllegalArgumentException ex) {
throw new BuildException(
"Invalid specification of test methods: \""
+ methodsList
+ "\"; expected: comma-separated list of valid Java identifiers",
ex);
}
}
}

/**
* Parses a comma-separated list of method names and check their validity.
* @param methodNames comma-separated list of method names to be parsed
* @return array of individual test method names
* @exception java.lang.IllegalArgumentException
* if the given string is <code>null</code> or if it is not
* a comma-separated list of valid Java identifiers;
* an empty string is acceptable and is handled as an empty
* list
* @since 1.8.2
*/
public static String[] parseTestMethodNamesList(String methodNames)
throws IllegalArgumentException {
if (methodNames == null) {
throw new IllegalArgumentException("methodNames is <null>");
}

methodNames = methodNames.trim();

int length = methodNames.length();
if (length == 0) {
return new String[0];
}

/* strip the trailing comma, if any */
if (methodNames.charAt(length - 1) == ',') {
methodNames = methodNames.substring(0, length - 1).trim();
length = methodNames.length();
if (length == 0) {
throw new IllegalArgumentException("Empty method name");
}
}

final char[] chars = methodNames.toCharArray();
/* easy detection of one particular case of illegal string: */
if (chars[0] == ',') {
throw new IllegalArgumentException("Empty method name");
}
/* count number of method names: */
int wordCount = 1;
for (int i = 1; i < chars.length; i++) {
if (chars[i] == ',') {
wordCount++;
}
}
/* prepare the resulting array: */
String[] result = new String[wordCount];
/* parse the string: */
final int stateBeforeWord = 1;
final int stateInsideWord = 2;
final int stateAfterWord = 3;
//
int state = stateBeforeWord;
int wordStartIndex = -1;
int wordIndex = 0;
for (int i = 0; i < chars.length; i++) {
char c = chars[i];
switch (state) {
case stateBeforeWord:
if (c == ',') {
throw new IllegalArgumentException("Empty method name");
} else if (c == ' ') {
// remain in the same state
} else if (Character.isJavaIdentifierStart(c)) {
wordStartIndex = i;
state = stateInsideWord;
} else {
throw new IllegalArgumentException("Illegal start of method name: " + c);
}
break;
case stateInsideWord:
if (c == ',') {
result[wordIndex++] = new String(methodNames.substring(wordStartIndex, i));
state = stateBeforeWord;
} else if (c == ' ') {
result[wordIndex++] = new String(methodNames.substring(wordStartIndex, i));
state = stateAfterWord;
} else if (Character.isJavaIdentifierPart(c)) {
// remain in the same state
} else {
throw new IllegalArgumentException("Illegal character in method name: " + c);
}
break;
case stateAfterWord:
if (c == ',') {
state = stateBeforeWord;
} else if (c == ' ') {
// remain in the same state
} else {
throw new IllegalArgumentException("Space in method name");
}
break;
default:
// this should never happen
}
}
switch (state) {
case stateBeforeWord:
case stateAfterWord:
break;
case stateInsideWord:
result[wordIndex++] = new String(methodNames.substring(wordStartIndex, chars.length));
break;
default:
// this should never happen
}
return result;
}

/**
* Get the name of the test class.
* @return the name of the test.


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

@@ -163,6 +163,9 @@ public class JUnitTestRunner implements TestListener, JUnitTaskMirror.JUnitTestR
*/
private static String crashFile = null;

/** Names of test methods to execute */
private String[] methods = null;
/**
* Constructor for fork=true or when the user hasn't specified a
* classpath.
@@ -205,7 +208,26 @@ public class JUnitTestRunner implements TestListener, JUnitTaskMirror.JUnitTestR
public JUnitTestRunner(JUnitTest test, boolean haltOnError,
boolean filtertrace, boolean haltOnFailure,
boolean showOutput, boolean logTestListenerEvents) {
this(test, haltOnError, filtertrace, haltOnFailure, showOutput,
this(test, null, haltOnError, filtertrace, haltOnFailure, showOutput,
logTestListenerEvents, null);
}

/**
* Constructor for fork=true or when the user hasn't specified a
* classpath.
* @param test the test to run.
* @param methods names of methods of the test to be executed.
* @param haltOnError whether to stop the run if an error is found.
* @param filtertrace whether to filter junit.*.* stack frames out of exceptions
* @param haltOnFailure whether to stop the run if failure is found.
* @param showOutput whether to send output to System.out/.err as well as formatters.
* @param logTestListenerEvents whether to print TestListener events.
* @since 1.8.2
*/
public JUnitTestRunner(JUnitTest test, String[] methods, boolean haltOnError,
boolean filtertrace, boolean haltOnFailure,
boolean showOutput, boolean logTestListenerEvents) {
this(test, methods, haltOnError, filtertrace, haltOnFailure, showOutput,
logTestListenerEvents, null);
}

@@ -254,12 +276,26 @@ public class JUnitTestRunner implements TestListener, JUnitTaskMirror.JUnitTestR
boolean filtertrace, boolean haltOnFailure,
boolean showOutput, boolean logTestListenerEvents,
ClassLoader loader) {
this(test, null, haltOnError, filtertrace, haltOnFailure, showOutput,
logTestListenerEvents, loader);
}


/**
* Constructor to use when the user has specified a classpath.
* @since 1.8.2
*/
public JUnitTestRunner(JUnitTest test, String[] methods, boolean haltOnError,
boolean filtertrace, boolean haltOnFailure,
boolean showOutput, boolean logTestListenerEvents,
ClassLoader loader) {
JUnitTestRunner.filtertrace = filtertrace;
this.junitTest = test;
this.haltOnError = haltOnError;
this.haltOnFailure = haltOnFailure;
this.showOutput = showOutput;
this.logTestListenerEvents = logTestListenerEvents;
this.methods = methods;
this.loader = loader;
}

@@ -340,9 +376,12 @@ public class JUnitTestRunner implements TestListener, JUnitTaskMirror.JUnitTestR
loader);
}

final boolean testMethodsSpecified = (methods != null);

// check for a static suite method first, even when using
// JUnit 4
Method suiteMethod = null;
if (!testMethodsSpecified) {
try {
// check if there is a suite method
suiteMethod = testClass.getMethod("suite", new Class[0]);
@@ -350,6 +389,7 @@ public class JUnitTestRunner implements TestListener, JUnitTaskMirror.JUnitTestR
// no appropriate suite method found. We don't report any
// error here since it might be perfectly normal.
}
}

if (suiteMethod != null) {
// if there is a suite method available, then try
@@ -359,7 +399,23 @@ public class JUnitTestRunner implements TestListener, JUnitTaskMirror.JUnitTestR

} else {
Class junit4TestAdapterClass = null;

boolean useSingleMethodAdapter = false;

if (junit.framework.TestCase.class.isAssignableFrom(testClass)) {
// Do not use JUnit 4 API for running JUnit 3.x
// tests - it is not able to run individual test
// methods.
//
// Technical details:
// org.junit.runner.Request.method(Class, String).getRunner()
// would return a runner which always executes all
// test methods. The reason is that the Runner would be
// an instance of class
// org.junit.internal.runners.OldTestClassRunner
// that does not implement interface Filterable - so it
// is unable to filter out test methods not matching
// the requested name.
} else {
// Check for JDK 5 first. Will *not* help on JDK 1.4
// if only junit-4.0.jar in CP because in that case
// linkage of whole task will already have failed! But
@@ -373,29 +429,69 @@ public class JUnitTestRunner implements TestListener, JUnitTaskMirror.JUnitTestR
if (loader == null) {
junit4TestAdapterClass =
Class.forName(JUNIT_4_TEST_ADAPTER);
if (testMethodsSpecified) {
/*
* We cannot try to load the JUnit4TestAdapter
* before trying to load JUnit4TestMethodAdapter
* because it might fail with
* NoClassDefFoundException, instead of plain
* ClassNotFoundException.
*/
junit4TestAdapterClass = Class.forName(
"org.apache.tools.ant.taskdefs.optional.junit.JUnit4TestMethodAdapter");
useSingleMethodAdapter = true;
}
} else {
junit4TestAdapterClass =
Class.forName(JUNIT_4_TEST_ADAPTER,
true, loader);
if (testMethodsSpecified) {
junit4TestAdapterClass =
Class.forName(
"org.apache.tools.ant.taskdefs.optional.junit.JUnit4TestMethodAdapter",
true, loader);
useSingleMethodAdapter = true;
}
}
} catch (ClassNotFoundException e) {
// OK, fall back to JUnit 3.
}
}
junit4 = junit4TestAdapterClass != null;

if (junit4) {
// Let's use it!
Class[] formalParams;
Object[] actualParams;
if (useSingleMethodAdapter) {
formalParams = new Class[] {Class.class, String[].class};
actualParams = new Object[] {testClass, methods};
} else {
formalParams = new Class[] {Class.class};
actualParams = new Object[] {testClass};
}
suite =
(Test) junit4TestAdapterClass
.getConstructor(new Class[] {Class.class}).
newInstance(new Object[] {testClass});
.getConstructor(formalParams).
newInstance(actualParams);
} else {
// Use JUnit 3.

// try to extract a test suite automatically this
// will generate warnings if the class is no
// suitable Test
suite = new TestSuite(testClass);
if (!testMethodsSpecified) {
suite = new TestSuite(testClass);
} else if (methods.length == 1) {
suite = TestSuite.createTest(testClass, methods[0]);
} else {
TestSuite testSuite = new TestSuite(testClass.getName());
for (int i = 0; i < methods.length; i++) {
testSuite.addTest(
TestSuite.createTest(testClass, methods[i]));
}
suite = testSuite;
}
}

}
@@ -670,11 +766,16 @@ public class JUnitTestRunner implements TestListener, JUnitTaskMirror.JUnitTestR
* <tr><td>logtestlistenerevents</td><td>log TestListener events to
* System.out.</td><td>false</td></tr>
*
* <tr><td>methods</td><td>Comma-separated list of names of individual
* test methods to execute.
* </td><td>null</td></tr>
*
* </table>
* @param args the command line arguments.
* @throws IOException on error.
*/
public static void main(String[] args) throws IOException {
String[] methods = null;
boolean haltError = false;
boolean haltFail = false;
boolean stackfilter = true;
@@ -696,7 +797,15 @@ public class JUnitTestRunner implements TestListener, JUnitTaskMirror.JUnitTestR
}

for (int i = 1; i < args.length; i++) {
if (args[i].startsWith(Constants.HALT_ON_ERROR)) {
if (args[i].startsWith(Constants.METHOD_NAMES)) {
try {
String methodsList = args[i].substring(Constants.METHOD_NAMES.length());
methods = JUnitTest.parseTestMethodNamesList(methodsList);
} catch (IllegalArgumentException ex) {
System.err.println("Invalid specification of test method names: " + args[i]);
System.exit(ERRORS);
}
} else if (args[i].startsWith(Constants.HALT_ON_ERROR)) {
haltError = Project.toBoolean(args[i].substring(Constants.HALT_ON_ERROR.length()));
} else if (args[i].startsWith(Constants.HALT_ON_FAILURE)) {
haltFail = Project.toBoolean(args[i].substring(Constants.HALT_ON_FAILURE.length()));
@@ -744,18 +853,30 @@ public class JUnitTestRunner implements TestListener, JUnitTaskMirror.JUnitTestR
java.io.BufferedReader reader =
new java.io.BufferedReader(new java.io.FileReader(args[0]));
String testCaseName;
String[] testMethodNames;
int code = 0;
boolean errorOccurred = false;
boolean failureOccurred = false;
String line = null;
while ((line = reader.readLine()) != null) {
StringTokenizer st = new StringTokenizer(line, ",");
testCaseName = st.nextToken();
String testListSpec = st.nextToken();
int colonIndex = testListSpec.indexOf(':');
if (colonIndex == -1) {
testCaseName = testListSpec;
testMethodNames = null;
} else {
testCaseName = testListSpec.substring(0, colonIndex);
testMethodNames = JUnitTest.parseTestMethodNamesList(
testListSpec
.substring(colonIndex + 1)
.replace('+', ','));
}
JUnitTest t = new JUnitTest(testCaseName);
t.setTodir(new File(st.nextToken()));
t.setOutfile(st.nextToken());
t.setProperties(props);
code = launch(t, haltError, stackfilter, haltFail,
code = launch(t, testMethodNames, haltError, stackfilter, haltFail,
showOut, outputToFormat,
logTestListenerEvents);
errorOccurred = (code == ERRORS);
@@ -783,7 +904,7 @@ public class JUnitTestRunner implements TestListener, JUnitTaskMirror.JUnitTestR
JUnitTest t = new JUnitTest(args[0]);
t.setProperties(props);
returnCode = launch(
t, haltError, stackfilter, haltFail,
t, methods, haltError, stackfilter, haltFail,
showOut, outputToFormat, logTestListenerEvents);
}

@@ -917,12 +1038,12 @@ public class JUnitTestRunner implements TestListener, JUnitTaskMirror.JUnitTestR
/**
* @since Ant 1.6.2
*/
private static int launch(JUnitTest t, boolean haltError,
private static int launch(JUnitTest t, String[] methods, boolean haltError,
boolean stackfilter, boolean haltFail,
boolean showOut, boolean outputToFormat,
boolean logTestListenerEvents) {
JUnitTestRunner runner =
new JUnitTestRunner(t, haltError, stackfilter, haltFail, showOut,
new JUnitTestRunner(t, methods, haltError, stackfilter, haltFail, showOut,
logTestListenerEvents, null);
runner.forked = true;
runner.outputToFormatters = outputToFormat;


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

@@ -0,0 +1,142 @@
/*
* 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.ComparisonFailure;
import junit.framework.TestCase;

/**
*
* @author Marian Petras
*/
public class BatchTestTest extends TestCase {
public BatchTestTest(String testName) {
super(testName);
}


public void testParseTestMethodNamesList() {
try {
JUnitTest.parseTestMethodNamesList(null);
fail("IllegalArgumentException expected when the param is <null>");
} catch (IllegalArgumentException ex) {
//this is an expected exception
}

assertEquals(new String[0], JUnitTest.parseTestMethodNamesList(""));
assertEquals(new String[0], JUnitTest.parseTestMethodNamesList(" "));
assertEquals(new String[0], JUnitTest.parseTestMethodNamesList(" "));

checkParseCausesIAE(",");
checkParseCausesIAE(" ,");
checkParseCausesIAE(", ");
checkParseCausesIAE(" , ");
checkParseCausesIAE(",a");
checkParseCausesIAE(" ,a");
checkParseCausesIAE(" ,a");
checkParseCausesIAE(" , a");
checkParseCausesIAE(" ,a ");
checkParseCausesIAE(" ,a ,");
checkParseCausesIAE("ab,,cd");
checkParseCausesIAE("ab, ,cd");
checkParseCausesIAE("ab, ,cd");
checkParseCausesIAE("ab, ,cd,");
checkParseCausesIAE(",ab, ,cd,");

assertEquals(new String[] {"abc"}, JUnitTest.parseTestMethodNamesList("abc"));
assertEquals(new String[] {"abc"}, JUnitTest.parseTestMethodNamesList("abc "));
assertEquals(new String[] {"abc"}, JUnitTest.parseTestMethodNamesList(" abc"));
assertEquals(new String[] {"abc"}, JUnitTest.parseTestMethodNamesList(" abc "));
assertEquals(new String[] {"abc"}, JUnitTest.parseTestMethodNamesList("abc "));
assertEquals(new String[] {"abc"}, JUnitTest.parseTestMethodNamesList("abc,"));
assertEquals(new String[] {"abc"}, JUnitTest.parseTestMethodNamesList("abc, "));
assertEquals(new String[] {"abc"}, JUnitTest.parseTestMethodNamesList("abc ,"));
assertEquals(new String[] {"abc"}, JUnitTest.parseTestMethodNamesList("abc , "));
assertEquals(new String[] {"abc"}, JUnitTest.parseTestMethodNamesList(" abc ,"));

/* legal Java identifiers: */
assertEquals(new String[] {"a"}, JUnitTest.parseTestMethodNamesList("a"));
assertEquals(new String[] {"a1"}, JUnitTest.parseTestMethodNamesList("a1"));
assertEquals(new String[] {"a$"}, JUnitTest.parseTestMethodNamesList("a$"));
assertEquals(new String[] {"a$1"}, JUnitTest.parseTestMethodNamesList("a$1"));
assertEquals(new String[] {"_bc"}, JUnitTest.parseTestMethodNamesList("_bc"));
assertEquals(new String[] {"___"}, JUnitTest.parseTestMethodNamesList("___"));

/* illegal Java identifiers: */
checkParseCausesIAE("1");
checkParseCausesIAE("1a");
checkParseCausesIAE("1ab");
checkParseCausesIAE("1abc");
checkParseCausesIAE("1abc d");
checkParseCausesIAE("1abc de");
checkParseCausesIAE("1abc def");
checkParseCausesIAE("1abc def,");
checkParseCausesIAE(",1abc def");

assertEquals(new String[] {"abc", "def"}, JUnitTest.parseTestMethodNamesList("abc,def"));
assertEquals(new String[] {"abc", "def"}, JUnitTest.parseTestMethodNamesList("abc,def,"));
assertEquals(new String[] {"abc", "def"}, JUnitTest.parseTestMethodNamesList("abc,def "));
assertEquals(new String[] {"abc", "def"}, JUnitTest.parseTestMethodNamesList("abc, def"));
assertEquals(new String[] {"abc", "def"}, JUnitTest.parseTestMethodNamesList("abc, def "));
assertEquals(new String[] {"abc", "def"}, JUnitTest.parseTestMethodNamesList("abc ,def"));
assertEquals(new String[] {"abc", "def"}, JUnitTest.parseTestMethodNamesList("abc ,def "));
assertEquals(new String[] {"abc", "def"}, JUnitTest.parseTestMethodNamesList("abc , def"));
assertEquals(new String[] {"abc", "def"}, JUnitTest.parseTestMethodNamesList("abc , def "));
assertEquals(new String[] {"abc", "def"}, JUnitTest.parseTestMethodNamesList(" abc,def"));
assertEquals(new String[] {"abc", "def"}, JUnitTest.parseTestMethodNamesList(" abc,def "));
assertEquals(new String[] {"abc", "def"}, JUnitTest.parseTestMethodNamesList(" abc, def"));
assertEquals(new String[] {"abc", "def"}, JUnitTest.parseTestMethodNamesList(" abc, def "));
assertEquals(new String[] {"abc", "def"}, JUnitTest.parseTestMethodNamesList(" abc ,def"));
assertEquals(new String[] {"abc", "def"}, JUnitTest.parseTestMethodNamesList(" abc ,def "));
assertEquals(new String[] {"abc", "def"}, JUnitTest.parseTestMethodNamesList(" abc , def"));
assertEquals(new String[] {"abc", "def"}, JUnitTest.parseTestMethodNamesList(" abc , def "));
assertEquals(new String[] {"abc", "def"}, JUnitTest.parseTestMethodNamesList(" abc , def ,"));
}

private static void checkParseCausesIAE(String param) {
try {
JUnitTest.parseTestMethodNamesList(param);
fail("IllegalArgumentException expected when the param is \"" + param + '"');
} catch (IllegalArgumentException ex) {
//this is an expected exception
}
}

private static void assertEquals(String[] expected, String[] actual) {
assertEquals(null, expected, actual);
}

private static void assertEquals(String message,
String[] expected,
String[] actual) {
if ((expected == null) && (actual == null)) {
return;
}
if (expected.length != actual.length) {
throw new ComparisonFailure(message,
expected.toString(),
actual.toString());
}
for (int i = 0; i < expected.length; i++) {
assertEquals(expected[i], actual[i]);
}
}

}

+ 45
- 3
src/tests/junit/org/apache/tools/ant/taskdefs/optional/junit/JUnitTestRunnerTest.java View File

@@ -33,6 +33,22 @@ public class JUnitTestRunnerTest extends TestCase {
super(name);
}

// check that a valid method name generates no errors
public void testValidMethod(){
TestRunner runner = createRunnerForTestMethod(ValidMethodTestCase.class,"testA");
runner.run();
assertEquals(runner.getFormatter().getError(), JUnitTestRunner.SUCCESS, runner.getRetCode());
}

// check that having an invalid method name generates an error
public void testInvalidMethod(){
TestRunner runner = createRunnerForTestMethod(InvalidMethodTestCase.class,"testInvalid");
runner.run();
String error = runner.getFormatter().getError();
// might be FAILURES or ERRORS depending on JUnit version?
assertTrue(error, runner.getRetCode() != JUnitTestRunner.SUCCESS);
}
// check that having no suite generates no errors
public void testNoSuite(){
TestRunner runner = createRunner(NoSuiteTestCase.class);
@@ -87,14 +103,22 @@ public class JUnitTestRunnerTest extends TestCase {
}

protected TestRunner createRunner(Class clazz){
return new TestRunner(new JUnitTest(clazz.getName()), true, true, true);
return new TestRunner(new JUnitTest(clazz.getName()), null,
true, true, true);
}

protected TestRunner createRunnerForTestMethod(Class clazz, String method){
return new TestRunner(new JUnitTest(clazz.getName()), new String[] {method},
true, true, true);
}
// the test runner that wrap the dummy formatter that interests us
private final static class TestRunner extends JUnitTestRunner {
private ResultFormatter formatter = new ResultFormatter();
TestRunner(JUnitTest test, boolean haltonerror, boolean filtertrace, boolean haltonfailure){
super(test, haltonerror, filtertrace, haltonfailure, TestRunner.class.getClassLoader());
TestRunner(JUnitTest test, String[] methods, boolean haltonerror,
boolean filtertrace, boolean haltonfailure){
super(test, methods, haltonerror, filtertrace, haltonfailure,
false, false, TestRunner.class.getClassLoader());
// use the classloader that loaded this class otherwise
// it will not be able to run inner classes if this test
// is ran in non-forked mode.
@@ -133,6 +157,24 @@ public class JUnitTestRunnerTest extends TestCase {
public static class NoTestCase {
}

public static class InvalidMethodTestCase extends TestCase {
public InvalidMethodTestCase(String name){ super(name); }
public void testA(){
throw new NullPointerException("thrown on purpose");
}
}

public static class ValidMethodTestCase extends TestCase {
public ValidMethodTestCase(String name){ super(name); }
public void testA(){
// expected to be executed
}
public void testB(){
// should not be executed
throw new NullPointerException("thrown on purpose");
}
}
public static class InvalidTestCase extends TestCase {
public InvalidTestCase(String name){
super(name);


Loading…
Cancel
Save