stack trace filtering for formatters. It will filter out those unneeded frame from Ant and JUnit so the stack will be much more readable. nb: Looks like the JUnit task need serious refactoring since we have been adding feature incrementally. It starts to be a real mess, I can barely read my own code :-( git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@270099 13f79535-47bb-0310-9956-ffa450edef68master
| @@ -60,12 +60,13 @@ import java.util.Vector; | |||
| /** | |||
| * Baseclass for BatchTest and JUnitTest. | |||
| * | |||
| * @author <a href="mailto:stefan.bodewig@epost.de">Stefan Bodewig</a> | |||
| * @author <a href="mailto:stefan.bodewig@epost.de">Stefan Bodewig</a> | |||
| * @author <a href="mailto:sbailliez@imediation.com">Stephane Bailliez</a> | |||
| */ | |||
| public abstract class BaseTest { | |||
| protected boolean haltOnError = false; | |||
| protected boolean haltOnFail = false; | |||
| protected boolean filtertrace = true; | |||
| protected boolean fork = false; | |||
| protected String ifProperty = null; | |||
| protected String unlessProperty = null; | |||
| @@ -76,6 +77,14 @@ public abstract class BaseTest { | |||
| protected String failureProperty; | |||
| protected String errorProperty; | |||
| public void setFiltertrace(boolean value) { | |||
| filtertrace = value; | |||
| } | |||
| public boolean getFiltertrace() { | |||
| return filtertrace; | |||
| } | |||
| public void setFork(boolean value) { | |||
| fork = value; | |||
| } | |||
| @@ -116,12 +125,12 @@ public abstract class BaseTest { | |||
| * Sets the destination directory. | |||
| */ | |||
| public void setTodir(File destDir) { | |||
| this.destDir = destDir; | |||
| this.destDir = destDir; | |||
| } | |||
| /** | |||
| * @return the destination directory as an absolute path if it exists | |||
| * otherwise return <tt>null</tt> | |||
| * otherwise return <tt>null</tt> | |||
| */ | |||
| public String getTodir(){ | |||
| if (destDir != null){ | |||
| @@ -133,15 +142,15 @@ public abstract class BaseTest { | |||
| public java.lang.String getFailureProperty() { | |||
| return failureProperty; | |||
| } | |||
| public void setFailureProperty(String failureProperty) { | |||
| this.failureProperty = failureProperty; | |||
| } | |||
| public java.lang.String getErrorProperty() { | |||
| return errorProperty; | |||
| } | |||
| public void setErrorProperty(String errorProperty) { | |||
| this.errorProperty = errorProperty; | |||
| } | |||
| @@ -199,6 +199,7 @@ public final class BatchTest extends BaseTest { | |||
| test.setName(classname); | |||
| test.setHaltonerror(this.haltOnError); | |||
| test.setHaltonfailure(this.haltOnFail); | |||
| test.setFiltertrace(this.filtertrace); | |||
| test.setFork(this.fork); | |||
| test.setIf(this.ifProperty); | |||
| test.setUnless(this.unlessProperty); | |||
| @@ -155,7 +155,23 @@ public class JUnitTask extends Task { | |||
| private Integer timeout = null; | |||
| private boolean summary = false; | |||
| private String summaryValue = ""; | |||
| private boolean filtertrace = true; | |||
| private JUnitTestRunner runner = null; | |||
| /** | |||
| * Tells this task whether to smartly filter the stack frames of JUnit testcase | |||
| * errors and failures before reporting them. This property is applied on all | |||
| * BatchTest (batchtest) and JUnitTest (test) however it can possibly be | |||
| * overridden by their own properties. | |||
| * @param value <tt>false</tt> if it should not filter, otherwise <tt>true<tt> | |||
| */ | |||
| public void setFiltertrace(boolean value) { | |||
| Enumeration enum = allTests(); | |||
| while (enum.hasMoreElements()) { | |||
| BaseTest test = (BaseTest) enum.nextElement(); | |||
| test.setFiltertrace(value); | |||
| } | |||
| } | |||
| /** | |||
| * Tells this task to halt when there is an error in a test. | |||
| @@ -361,7 +377,7 @@ public class JUnitTask extends Task { | |||
| /** | |||
| * Adds the jars or directories containing Ant, this task and | |||
| * JUnit to the classpath - this should make the forked JVM work | |||
| * without having to specify the directly. | |||
| * without having to specify them directly. | |||
| */ | |||
| public void init() { | |||
| addClasspathEntry("/junit/framework/TestCase.class"); | |||
| @@ -447,6 +463,7 @@ public class JUnitTask extends Task { | |||
| cmd.setClassname("org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner"); | |||
| cmd.createArgument().setValue(test.getName()); | |||
| cmd.createArgument().setValue("filtertrace=" + test.getFiltertrace()); | |||
| cmd.createArgument().setValue("haltOnError=" + test.getHaltonerror()); | |||
| cmd.createArgument().setValue("haltOnFailure=" + test.getHaltonfailure()); | |||
| if (summary) { | |||
| @@ -555,8 +572,7 @@ public class JUnitTask extends Task { | |||
| // will cause trouble in JDK 1.1 if omitted | |||
| cl.addSystemPackageRoot("org.apache.tools.ant"); | |||
| } | |||
| runner = new JUnitTestRunner(test, test.getHaltonerror(), test.getHaltonfailure(), cl); | |||
| runner = new JUnitTestRunner(test, test.getHaltonerror(), test.getFiltertrace(), test.getHaltonfailure(), cl); | |||
| if (summary) { | |||
| log("Running " + test.getName(), Project.MSG_INFO); | |||
| @@ -100,10 +100,11 @@ public class JUnitTest extends BaseTest { | |||
| this.name = name; | |||
| } | |||
| public JUnitTest(String name, boolean haltOnError, boolean haltOnFailure) { | |||
| public JUnitTest(String name, boolean haltOnError, boolean haltOnFailure, boolean filtertrace) { | |||
| this.name = name; | |||
| this.haltOnError = haltOnError; | |||
| this.haltOnFail = haltOnFail; | |||
| this.haltOnFail = haltOnFailure; | |||
| this.filtertrace = filtertrace; | |||
| } | |||
| /** | |||
| @@ -64,7 +64,11 @@ import junit.framework.Test; | |||
| import junit.framework.TestSuite; | |||
| import junit.framework.AssertionFailedError; | |||
| import java.lang.reflect.Method; | |||
| import java.io.BufferedReader; | |||
| import java.io.PrintStream; | |||
| import java.io.PrintWriter; | |||
| import java.io.StringReader; | |||
| import java.io.StringWriter; | |||
| import java.io.ByteArrayOutputStream; | |||
| import java.io.IOException; | |||
| import java.io.FileInputStream; | |||
| @@ -121,6 +125,24 @@ public class JUnitTestRunner implements TestListener { | |||
| */ | |||
| private TestResult res; | |||
| /** | |||
| * Do we filter junit.*.* stack frames out of failure and error exceptions. | |||
| */ | |||
| private static boolean filtertrace = true; | |||
| private final static String[] DEFAULT_TRACE_FILTERS = new String[] { | |||
| "junit.framework.TestCase", | |||
| "junit.framework.TestResult", | |||
| "junit.framework.TestSuite", | |||
| "junit.framework.Assert.", // don't filter AssertionFailure | |||
| "junit.swingui.TestRunner", | |||
| "junit.awtui.TestRunner", | |||
| "junit.textui.TestRunner", | |||
| "java.lang.reflect.Method.invoke(", | |||
| "org.apache.tools.ant." | |||
| }; | |||
| /** | |||
| * Do we stop on errors. | |||
| */ | |||
| @@ -161,16 +183,18 @@ public class JUnitTestRunner implements TestListener { | |||
| * Constructor for fork=true or when the user hasn't specified a | |||
| * classpath. | |||
| */ | |||
| public JUnitTestRunner(JUnitTest test, boolean haltOnError, | |||
| public JUnitTestRunner(JUnitTest test, boolean haltOnError, boolean filtertrace, | |||
| boolean haltOnFailure) { | |||
| this(test, haltOnError, haltOnFailure, null); | |||
| this(test, haltOnError, filtertrace, haltOnFailure, null); | |||
| } | |||
| /** | |||
| * Constructor to use when the user has specified a classpath. | |||
| */ | |||
| public JUnitTestRunner(JUnitTest test, boolean haltOnError, | |||
| public JUnitTestRunner(JUnitTest test, boolean haltOnError, boolean filtertrace, | |||
| boolean haltOnFailure, ClassLoader loader) { | |||
| //JUnitTestRunner.filtertrace = filtertrace; | |||
| this.filtertrace = filtertrace; | |||
| this.junitTest = test; | |||
| this.haltOnError = haltOnError; | |||
| this.haltOnFailure = haltOnFailure; | |||
| @@ -378,6 +402,7 @@ public class JUnitTestRunner implements TestListener { | |||
| boolean exitAtEnd = true; | |||
| boolean haltError = false; | |||
| boolean haltFail = false; | |||
| boolean stackfilter = true; | |||
| Properties props = new Properties(); | |||
| if (args.length == 0) { | |||
| @@ -390,6 +415,8 @@ public class JUnitTestRunner implements TestListener { | |||
| haltError = Project.toBoolean(args[i].substring(12)); | |||
| } else if (args[i].startsWith("haltOnFailure=")) { | |||
| haltFail = Project.toBoolean(args[i].substring(14)); | |||
| } else if (args[i].startsWith("filtertrace=")) { | |||
| stackfilter = Project.toBoolean(args[i].substring(12)); | |||
| } else if (args[i].startsWith("formatter=")) { | |||
| try { | |||
| createAndStoreFormatter(args[i].substring(10)); | |||
| @@ -405,7 +432,7 @@ public class JUnitTestRunner implements TestListener { | |||
| } | |||
| JUnitTest t = new JUnitTest(args[0]); | |||
| // Add/overlay system properties on the properties from the Ant project | |||
| Hashtable p = System.getProperties(); | |||
| for (Enumeration enum = p.keys(); enum.hasMoreElements(); ) { | |||
| @@ -414,7 +441,7 @@ public class JUnitTestRunner implements TestListener { | |||
| } | |||
| t.setProperties(props); | |||
| JUnitTestRunner runner = new JUnitTestRunner(t, haltError, haltFail); | |||
| JUnitTestRunner runner = new JUnitTestRunner(t, haltError, stackfilter, haltFail); | |||
| transferFormatters(runner); | |||
| runner.run(); | |||
| System.exit(runner.getRetCode()); | |||
| @@ -443,5 +470,53 @@ public class JUnitTestRunner implements TestListener { | |||
| } | |||
| fromCmdLine.addElement(fe.createFormatter()); | |||
| } | |||
| /** | |||
| * Returns a filtered stack trace. | |||
| * This is ripped out of junit.runner.BaseTestRunner. | |||
| * Scott M. Stirling. | |||
| */ | |||
| public static String getFilteredTrace(Throwable t) { | |||
| StringWriter stringWriter = new StringWriter(); | |||
| PrintWriter writer = new PrintWriter(stringWriter); | |||
| t.printStackTrace(writer); | |||
| StringBuffer buffer = stringWriter.getBuffer(); | |||
| String trace = buffer.toString(); | |||
| return JUnitTestRunner.filterStack(trace); | |||
| } | |||
| /** | |||
| * Filters stack frames from internal JUnit and Ant classes | |||
| */ | |||
| public static String filterStack(String stack) { | |||
| if (!filtertrace) { | |||
| return stack; | |||
| } | |||
| StringWriter sw = new StringWriter(); | |||
| PrintWriter pw = new PrintWriter(sw); | |||
| StringReader sr = new StringReader(stack); | |||
| BufferedReader br = new BufferedReader(sr); | |||
| String line; | |||
| try { | |||
| while ((line = br.readLine()) != null) { | |||
| if (!filterLine(line)) { | |||
| pw.println(line); | |||
| } | |||
| } | |||
| } catch (Exception IOException) { | |||
| return stack; // return the stack unfiltered | |||
| } | |||
| return sw.toString(); | |||
| } | |||
| private static boolean filterLine(String line) { | |||
| for (int i = 0; i < DEFAULT_TRACE_FILTERS.length; i++) { | |||
| if (line.indexOf(DEFAULT_TRACE_FILTERS[i]) > 0) { | |||
| return true; | |||
| } | |||
| } | |||
| return false; | |||
| } | |||
| } // JUnitTestRunner | |||
| @@ -248,7 +248,8 @@ public class PlainJUnitResultFormatter implements JUnitResultFormatter { | |||
| wri.println(type); | |||
| wri.println(t.getMessage()); | |||
| t.printStackTrace(wri); | |||
| String strace = JUnitTestRunner.getFilteredTrace(t); | |||
| wri.print(strace); | |||
| wri.println(""); | |||
| } | |||
| } | |||
| @@ -261,9 +261,8 @@ public class XMLJUnitResultFormatter implements JUnitResultFormatter, XMLConstan | |||
| } | |||
| nested.setAttribute(ATTR_TYPE, t.getClass().getName()); | |||
| StringWriter swr = new StringWriter(); | |||
| t.printStackTrace(new PrintWriter(swr, true)); | |||
| Text trace = doc.createTextNode(swr.toString()); | |||
| String strace = JUnitTestRunner.getFilteredTrace(t); | |||
| Text trace = doc.createTextNode(strace); | |||
| nested.appendChild(trace); | |||
| } | |||