Author: jglick Date: Tue Feb 28 13:07:15 2006 New Revision: 381780 URL: http://svn.apache.org/viewcvs?rev=381780&view=rev Log: #38811: support for JUnit 4.0.
Modified: ant/core/trunk/WHATSNEW ant/core/trunk/docs/manual/OptionalTasks/junit.html ant/core/trunk/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTestRunner.java ant/core/trunk/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitVersionHelper.java Modified: ant/core/trunk/WHATSNEW URL: http://svn.apache.org/viewcvs/ant/core/trunk/WHATSNEW?rev=381780&r1=381779&r2=381780&view=diff ============================================================================== --- ant/core/trunk/WHATSNEW (original) +++ ant/core/trunk/WHATSNEW Tue Feb 28 13:07:15 2006 @@ -77,6 +77,8 @@ Fixed bugs: ----------- +* <junit> now supports JUnit 4. Bugzilla Report 38811. + * <junit> can now work with junit.jar in its <classpath>. Bugzilla Report 38799. * Some potential NullPointerExceptions, Bugzilla Reports 37765 and 38056 Modified: ant/core/trunk/docs/manual/OptionalTasks/junit.html URL: http://svn.apache.org/viewcvs/ant/core/trunk/docs/manual/OptionalTasks/junit.html?rev=381780&r1=381779&r2=381780&view=diff ============================================================================== --- ant/core/trunk/docs/manual/OptionalTasks/junit.html (original) +++ ant/core/trunk/docs/manual/OptionalTasks/junit.html Tue Feb 28 13:07:15 2006 @@ -12,7 +12,8 @@ version of the framework can be found at <a href="http://www.junit.org">http://www.junit.org</a>. This task has been tested with JUnit 3.0 up to JUnit 3.8.1; it won't -work with versions prior to JUnit 3.0.</p> +work with versions prior to JUnit 3.0. It also works with JUnit 4.0, including +"pure" JUnit 4 tests using only annotations and no <code>JUnit4TestAdapter</code>.</p> <p><strong>Note:</strong> This task depends on external libraries not included in the Ant distribution. See <a href="../install.html#librarydependencies"> Library Dependencies</a> for more information. Modified: ant/core/trunk/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTestRunner.java URL: http://svn.apache.org/viewcvs/ant/core/trunk/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTestRunner.java?rev=381780&r1=381779&r2=381780&view=diff ============================================================================== --- ant/core/trunk/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTestRunner.java (original) +++ ant/core/trunk/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTestRunner.java Tue Feb 28 13:07:15 2006 @@ -35,6 +35,7 @@ import java.util.Vector; import junit.framework.AssertionFailedError; import junit.framework.Test; +import junit.framework.TestFailure; import junit.framework.TestListener; import junit.framework.TestResult; import junit.framework.TestSuite; @@ -99,7 +100,13 @@ "junit.textui.TestRunner", "java.lang.reflect.Method.invoke(", "sun.reflect.", - "org.apache.tools.ant." + "org.apache.tools.ant.", + // JUnit 4 support: + "org.junit.", + "junit.framework.JUnit4TestAdapter", + // See wrapListener for reason: + "Caused by: java.lang.AssertionError", + " more", }; @@ -141,6 +148,9 @@ /** Do we print TestListener events? */ private boolean logTestListenerEvents = false; + /** Turned on if we are using JUnit 4 for this test suite. see #38811 */ + private boolean junit4; + /** * Constructor for fork=true or when the user hasn't specified a * classpath. @@ -212,9 +222,9 @@ public void run() { res = new TestResult(); - res.addListener(this); + res.addListener(wrapListener(this)); for (int i = 0; i < formatters.size(); i++) { - res.addListener((TestListener) formatters.elementAt(i)); + res.addListener(wrapListener((TestListener) formatters.elementAt(i))); } ByteArrayOutputStream errStrm = new ByteArrayOutputStream(); @@ -255,6 +265,19 @@ try { try { + Class junit4TestAdapterClass = null; + // Note that checking for JDK 5 directly won't work; under JDK 4, this will already have failed. + try { + if (loader == null) { + junit4TestAdapterClass = Class.forName("junit.framework.JUnit4TestAdapter"); + } else { + junit4TestAdapterClass = Class.forName("junit.framework.JUnit4TestAdapter", true, loader); + } + } catch (ClassNotFoundException e) { + // OK, fall back to JUnit 3. + } + junit4 = junit4TestAdapterClass != null; + Class testClass = null; if (loader == null) { testClass = Class.forName(junitTest.getName()); @@ -263,24 +286,33 @@ loader); } - 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 (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]); + if (junit4) { + // Let's use it! + suite = (Test) junit4TestAdapterClass.getConstructor(new Class[] {Class.class}). + newInstance(new Object[] {testClass}); } else { - // try to extract a test suite automatically this - // will generate warnings if the class is no - // suitable Test - suite = new TestSuite(testClass); + // Use JUnit 3. + + 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 (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) { @@ -303,8 +335,13 @@ logTestListenerEvent("tests to run: " + suite.countTestCases()); suite.run(res); } finally { - junitTest.setCounts(res.runCount(), res.failureCount(), - res.errorCount()); + if (junit4) { + int[] cnts = findJUnit4FailureErrorCount(res); + junitTest.setCounts(res.runCount(), cnts[0], cnts[1]); + } else { + junitTest.setCounts(res.runCount(), res.failureCount(), + res.errorCount()); + } junitTest.setRunTime(System.currentTimeMillis() - start); } } @@ -692,7 +729,7 @@ private static boolean filterLine(String line) { for (int i = 0; i < DEFAULT_TRACE_FILTERS.length; i++) { - if (line.indexOf(DEFAULT_TRACE_FILTERS[i]) > 0) { + if (line.indexOf(DEFAULT_TRACE_FILTERS[i]) != -1) { return true; } } @@ -733,4 +770,83 @@ } } } + + /** + * Modifies a TestListener when running JUnit 4: + * treats AssertionFailedError as a failure not an error. + * @since Ant 1.7 + */ + private TestListener wrapListener(final TestListener testListener) { + return new TestListener() { + public void addError(Test test, Throwable t) { + if (junit4 && t instanceof AssertionFailedError) { + // JUnit 4 does not distinguish between errors and failures even in the JUnit 3 adapter. + // So we need to help it a bit to retain compatibility for JUnit 3 tests. + testListener.addFailure(test, (AssertionFailedError) t); + } else if (junit4 && t.getClass().getName().equals("java.lang.AssertionError")) { + // Not strictly necessary but probably desirable. + // JUnit 4-specific test GUIs will show just "failures". + // But Ant's output shows "failures" vs. "errors". + // We would prefer to show "failure" for things that logically are. + try { + String msg = t.getMessage(); + AssertionFailedError failure = msg != null ? + new AssertionFailedError(msg) : new AssertionFailedError(); + // To compile on pre-JDK 4 (even though this should always succeed): + Method initCause = Throwable.class.getMethod("initCause", new Class[] {Throwable.class}); + initCause.invoke(failure, new Object[] {t}); + testListener.addFailure(test, failure); + } catch (Exception e) { + // Rats. + e.printStackTrace(); // should not happen + testListener.addError(test, t); + } + } else { + testListener.addError(test, t); + } + } + public void addFailure(Test test, AssertionFailedError t) { + testListener.addFailure(test, t); + } + public void addFailure(Test test, Throwable t) { // pre-3.4 + if (t instanceof AssertionFailedError) { + testListener.addFailure(test, (AssertionFailedError) t); + } else { + testListener.addError(test, t); + } + } + public void endTest(Test test) { + testListener.endTest(test); + } + public void startTest(Test test) { + testListener.startTest(test); + } + }; + } + + /** + * Use instead of TestResult.get{Failure,Error}Count on JUnit 4, + * since the adapter claims that all failures are errors. + * @since Ant 1.7 + */ + private int[] findJUnit4FailureErrorCount(TestResult res) { + int failures = 0; + int errors = 0; + Enumeration e = res.failures(); + while (e.hasMoreElements()) { + e.nextElement(); + failures++; + } + e = res.errors(); + while (e.hasMoreElements()) { + Throwable t = ((TestFailure) e.nextElement()).thrownException(); + if (t instanceof AssertionFailedError || t.getClass().getName().equals("java.lang.AssertionError")) { + failures++; + } else { + errors++; + } + } + return new int[] {failures, errors}; + } + } // JUnitTestRunner Modified: ant/core/trunk/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitVersionHelper.java URL: http://svn.apache.org/viewcvs/ant/core/trunk/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitVersionHelper.java?rev=381780&r1=381779&r2=381780&view=diff ============================================================================== --- ant/core/trunk/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitVersionHelper.java (original) +++ ant/core/trunk/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitVersionHelper.java Tue Feb 28 13:07:15 2006 @@ -1,5 +1,5 @@ /* - * Copyright 2001-2002,2004-2005 The Apache Software Foundation + * Copyright 2001-2002,2004-2006 The Apache Software Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -51,8 +51,21 @@ * <p>since Ant 1.5.1 this method will invoke "<code>public * String getName()</code>" on any implementation of Test if * it exists.</p> + * + * <p>Since Ant 1.7 also checks for JUnit4TestCaseFacade explicitly. + * This is used by junit.framework.JUnit4TestAdapter.</p> */ public static String getTestCaseName(Test t) { + if (t != null && t.getClass().getName().equals("junit.framework.JUnit4TestCaseFacade")) { + // Self-describing as of JUnit 4 (#38811). But trim "(ClassName)". + String name = t.toString(); + if (name.endsWith(")")) { + int paren = name.lastIndexOf('('); + return name.substring(0, paren); + } else { + return name; + } + } if (t instanceof TestCase && testCaseName != null) { try { return (String) testCaseName.invoke(t, new Object[0]); --------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]