Index: docs/manual/OptionalTasks/junit.html =================================================================== RCS file: /home/cvspublic/ant/docs/manual/OptionalTasks/junit.html,v --- docs/manual/OptionalTasks/junit.html 29 Apr 2005 18:58:09 -0000 1.40 +++ docs/manual/OptionalTasks/junit.html 6 May 2005 00:23:43 -0000 @@ -378,6 +378,23 @@ Yes + method + Name of a single test case method to execute. Since Ant 1.7

+ The method attribute can be useful in the following scenarios: +

+ + No + + fork Run the tests in a separate VM. Overrides value set in <junit>. Index: src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTask.java =================================================================== RCS file: /home/cvspublic/ant/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTask.java,v --- src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTask.java 30 Mar 2005 17:06:39 -0000 1.117 +++ src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTask.java 8 May 2005 03:47:49 -0000 @@ -802,6 +802,9 @@ cmd.createArgument().setValue("haltOnError=" + test.getHaltonerror()); cmd.createArgument().setValue("haltOnFailure=" + test.getHaltonfailure()); + cmd.createArgument().setValue("method=" + + ( (test.getMethod() != null) ? test.getMethod() : "" ) ); + if (includeAntRuntime) { Vector v = Execute.getProcEnvironment(); Enumeration e = v.elements(); @@ -821,6 +824,7 @@ } if (summary) { + // TODO should we log a different message here if test.getMethod() != null ? log("Running " + test.getName(), Project.MSG_INFO); String prefix = ""; if ("withoutanderr".equalsIgnoreCase(summaryValue)) { @@ -1069,11 +1073,15 @@ if (classLoader != null) { classLoader.setThreadContextLoader(); } - runner = new JUnitTestRunner(test, test.getHaltonerror(), + runner = new JUnitTestRunner(test, + test.getMethod(), + test.getHaltonerror(), test.getFiltertrace(), test.getHaltonfailure(), false, - true, classLoader); + true, + classLoader); if (summary) { + // TODO should we log a different message here if test.getMethod() != null ? log("Running " + test.getName(), Project.MSG_INFO); SummaryJUnitResultFormatter f = Index: src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTest.java =================================================================== RCS file: /home/cvspublic/ant/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTest.java,v --- src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTest.java 9 Mar 2004 16:48:31 -0000 1.23 +++ src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTest.java 6 May 2005 05:33:55 -0000 @@ -39,6 +39,9 @@ /** the name of the test case */ private String name = null; + /** the name of a test method to execute */ + private String method = null; + /** the name of the result file */ private String outfile = null; @@ -59,11 +62,24 @@ } public JUnitTest(String name, boolean haltOnError, boolean haltOnFailure, - boolean filtertrace) { + boolean filtertrace) { + this(name, haltOnError, haltOnFailure, filtertrace, null); + } + + public JUnitTest(String name, boolean haltOnError, boolean haltOnFailure, + boolean filtertrace, String method) { this.name = name; this.haltOnError = haltOnError; this.haltOnFail = haltOnFailure; this.filtertrace = filtertrace; + this.method = method; + } + + /** + * Set the name of the individual test method to be invoked. + */ + public void setMethod(String value) { + method = value; } /** @@ -81,6 +97,15 @@ } /** + * Get the name of the individual test method to be invoked. + * + * @return the name of the individual test method to be invoked. + */ + public String getMethod() { + return method; + } + + /** * Get the name of the test class. */ public String getName() { Index: src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTestRunner.java =================================================================== RCS file: /home/cvspublic/ant/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTestRunner.java,v --- src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTestRunner.java 30 Mar 2005 17:08:27 -0000 1.58 +++ src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTestRunner.java 8 May 2005 01:27:06 -0000 @@ -165,6 +165,9 @@ /** Do we print TestListener events? */ private boolean logTestListenerEvents = false; + /** Name of single test method to execute */ + private String method = null; + /** * Constructor for fork=true or when the user hasn't specified a * classpath. @@ -192,11 +195,23 @@ 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. + * @since Ant 1.7 + */ + public JUnitTestRunner(JUnitTest test, String method, boolean haltOnError, + boolean filtertrace, boolean haltOnFailure, + boolean showOutput, boolean logTestListenerEvents) { + this(test, method, haltOnError, filtertrace, haltOnFailure, showOutput, + logTestListenerEvents, null); + } + + /** * Constructor to use when the user has specified a classpath. */ public JUnitTestRunner(JUnitTest test, boolean haltOnError, @@ -223,12 +238,26 @@ 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 Ant 1.7 + */ + public JUnitTestRunner(JUnitTest test, String method, 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.method = method; this.loader = loader; } @@ -288,25 +317,35 @@ } Method suiteMethod = null; - try { - // check if there is a suite method - suiteMethod = testClass.getMethod("suite", new Class[0]); - } catch (NoSuchMethodException e) { - // no appropriate suite method found. We don't report any - // error here since it might be perfectly normal. + if (method != null && method.length() > 0) + { + // we are invoking a specific test method + // ensure ERROR is generated in catch block if method doesn't exist + testClass.getMethod(method, new Class[0]); // throws NoSuchMethodException + suite = TestSuite.createTest(testClass, method); } - if (suiteMethod != null) { - // if there is a suite method available, then try - // to extract the suite from it. If there is an error - // here it will be caught below and reported. - suite = (Test) suiteMethod.invoke(null, new Class[0]); - } else { - // try to extract a test suite automatically this - // will generate warnings if the class is no - // suitable Test - suite = new TestSuite(testClass); + else + { + try { + // check if there is a suite method + suiteMethod = testClass.getMethod("suite", new Class[0]); + } catch (NoSuchMethodException e) { + // 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 + // to extract the suite from it. If there is an error + // here it will be caught below and reported. + suite = (Test) suiteMethod.invoke(null, new Class[0]); + } else { + // try to extract a test suite automatically this + // will generate warnings if the class is no + // suitable Test + suite = new TestSuite(testClass); + } } - } catch (Throwable e) { retCode = ERRORS; exception = e; @@ -530,6 +569,10 @@ * logtestlistenereventslog TestListener events to * System.out.false * + * methodThe name of an individual test method + * to execute. + * null + * * */ public static void main(String[] args) throws IOException { @@ -540,6 +583,7 @@ boolean showOut = false; boolean logTestListenerEvents = false; String noCrashFile = null; + String method = null; if (args.length == 0) { System.err.println("required argument TestClassName missing"); @@ -576,6 +620,8 @@ showOut = Project.toBoolean(args[i].substring(11)); } else if (args[i].startsWith("logtestlistenerevents=")) { logTestListenerEvents = Project.toBoolean(args[i].substring(22)); + } else if (args[i].startsWith("method=")) { + method = args[i].substring(7); } } @@ -602,7 +648,7 @@ JUnitTest t = new JUnitTest(testCaseName); t.setTodir(new File(st.nextToken())); t.setOutfile(st.nextToken()); - code = launch(t, haltError, stackfilter, haltFail, + code = launch(t, null, haltError, stackfilter, haltFail, showOut, logTestListenerEvents, props); errorOccurred = (code == ERRORS); failureOccurred = (code != SUCCESS); @@ -624,7 +670,7 @@ e.printStackTrace(); } } else { - returnCode = launch(new JUnitTest(args[0]), haltError, + returnCode = launch(new JUnitTest(args[0]), method, haltError, stackfilter, haltFail, showOut, logTestListenerEvents, props); } @@ -722,14 +768,14 @@ /** * @since Ant 1.6.2 */ - private static int launch(JUnitTest t, boolean haltError, + private static int launch(JUnitTest t, String method, boolean haltError, boolean stackfilter, boolean haltFail, boolean showOut, boolean logTestListenerEvents, Properties props) { t.setProperties(props); JUnitTestRunner runner = - new JUnitTestRunner(t, haltError, stackfilter, haltFail, showOut, - logTestListenerEvents); + new JUnitTestRunner(t, method, haltError, stackfilter, haltFail, + showOut, logTestListenerEvents); runner.forked = true; transferFormatters(runner, t); Index: src/testcases/org/apache/tools/ant/taskdefs/optional/junit/JUnitTestRunnerTest.java =================================================================== RCS file: /home/cvspublic/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/junit/JUnitTestRunnerTest.java,v --- src/testcases/org/apache/tools/ant/taskdefs/optional/junit/JUnitTestRunnerTest.java 9 Mar 2004 16:49:02 -0000 1.10 +++ src/testcases/org/apache/tools/ant/taskdefs/optional/junit/JUnitTestRunnerTest.java 7 May 2005 12:48:25 -0000 @@ -18,6 +18,7 @@ import java.io.*; import junit.framework.*; + import org.apache.tools.ant.BuildException; /** @@ -32,6 +33,21 @@ 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(); + assertEquals(error, JUnitTestRunner.ERRORS, runner.getRetCode()); + } + // check that having no suite generates no errors public void testNoSuite(){ TestRunner runner = createRunner(NoSuiteTestCase.class); @@ -74,14 +90,22 @@ } 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()), 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 method, boolean haltonerror, + boolean filtertrace, boolean haltonfailure){ + super(test, method, 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. @@ -120,6 +144,24 @@ 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);