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. | * 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> | * @author <a href="mailto:sbailliez@imediation.com">Stephane Bailliez</a> | ||||
| */ | */ | ||||
| public abstract class BaseTest { | public abstract class BaseTest { | ||||
| protected boolean haltOnError = false; | protected boolean haltOnError = false; | ||||
| protected boolean haltOnFail = false; | protected boolean haltOnFail = false; | ||||
| protected boolean filtertrace = true; | |||||
| protected boolean fork = false; | protected boolean fork = false; | ||||
| protected String ifProperty = null; | protected String ifProperty = null; | ||||
| protected String unlessProperty = null; | protected String unlessProperty = null; | ||||
| @@ -76,6 +77,14 @@ public abstract class BaseTest { | |||||
| protected String failureProperty; | protected String failureProperty; | ||||
| protected String errorProperty; | protected String errorProperty; | ||||
| public void setFiltertrace(boolean value) { | |||||
| filtertrace = value; | |||||
| } | |||||
| public boolean getFiltertrace() { | |||||
| return filtertrace; | |||||
| } | |||||
| public void setFork(boolean value) { | public void setFork(boolean value) { | ||||
| fork = value; | fork = value; | ||||
| } | } | ||||
| @@ -116,12 +125,12 @@ public abstract class BaseTest { | |||||
| * Sets the destination directory. | * Sets the destination directory. | ||||
| */ | */ | ||||
| public void setTodir(File destDir) { | public void setTodir(File destDir) { | ||||
| this.destDir = destDir; | |||||
| this.destDir = destDir; | |||||
| } | } | ||||
| /** | /** | ||||
| * @return the destination directory as an absolute path if it exists | * @return the destination directory as an absolute path if it exists | ||||
| * otherwise return <tt>null</tt> | |||||
| * otherwise return <tt>null</tt> | |||||
| */ | */ | ||||
| public String getTodir(){ | public String getTodir(){ | ||||
| if (destDir != null){ | if (destDir != null){ | ||||
| @@ -133,15 +142,15 @@ public abstract class BaseTest { | |||||
| public java.lang.String getFailureProperty() { | public java.lang.String getFailureProperty() { | ||||
| return failureProperty; | return failureProperty; | ||||
| } | } | ||||
| public void setFailureProperty(String failureProperty) { | public void setFailureProperty(String failureProperty) { | ||||
| this.failureProperty = failureProperty; | this.failureProperty = failureProperty; | ||||
| } | } | ||||
| public java.lang.String getErrorProperty() { | public java.lang.String getErrorProperty() { | ||||
| return errorProperty; | return errorProperty; | ||||
| } | } | ||||
| public void setErrorProperty(String errorProperty) { | public void setErrorProperty(String errorProperty) { | ||||
| this.errorProperty = errorProperty; | this.errorProperty = errorProperty; | ||||
| } | } | ||||
| @@ -199,6 +199,7 @@ public final class BatchTest extends BaseTest { | |||||
| test.setName(classname); | test.setName(classname); | ||||
| test.setHaltonerror(this.haltOnError); | test.setHaltonerror(this.haltOnError); | ||||
| test.setHaltonfailure(this.haltOnFail); | test.setHaltonfailure(this.haltOnFail); | ||||
| test.setFiltertrace(this.filtertrace); | |||||
| test.setFork(this.fork); | test.setFork(this.fork); | ||||
| test.setIf(this.ifProperty); | test.setIf(this.ifProperty); | ||||
| test.setUnless(this.unlessProperty); | test.setUnless(this.unlessProperty); | ||||
| @@ -155,7 +155,23 @@ public class JUnitTask extends Task { | |||||
| private Integer timeout = null; | private Integer timeout = null; | ||||
| private boolean summary = false; | private boolean summary = false; | ||||
| private String summaryValue = ""; | private String summaryValue = ""; | ||||
| private boolean filtertrace = true; | |||||
| private JUnitTestRunner runner = null; | 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. | * 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 | * Adds the jars or directories containing Ant, this task and | ||||
| * JUnit to the classpath - this should make the forked JVM work | * 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() { | public void init() { | ||||
| addClasspathEntry("/junit/framework/TestCase.class"); | 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.setClassname("org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner"); | ||||
| cmd.createArgument().setValue(test.getName()); | cmd.createArgument().setValue(test.getName()); | ||||
| cmd.createArgument().setValue("filtertrace=" + test.getFiltertrace()); | |||||
| cmd.createArgument().setValue("haltOnError=" + test.getHaltonerror()); | cmd.createArgument().setValue("haltOnError=" + test.getHaltonerror()); | ||||
| cmd.createArgument().setValue("haltOnFailure=" + test.getHaltonfailure()); | cmd.createArgument().setValue("haltOnFailure=" + test.getHaltonfailure()); | ||||
| if (summary) { | if (summary) { | ||||
| @@ -555,8 +572,7 @@ public class JUnitTask extends Task { | |||||
| // will cause trouble in JDK 1.1 if omitted | // will cause trouble in JDK 1.1 if omitted | ||||
| cl.addSystemPackageRoot("org.apache.tools.ant"); | 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) { | if (summary) { | ||||
| log("Running " + test.getName(), Project.MSG_INFO); | log("Running " + test.getName(), Project.MSG_INFO); | ||||
| @@ -100,10 +100,11 @@ public class JUnitTest extends BaseTest { | |||||
| this.name = name; | 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.name = name; | ||||
| this.haltOnError = haltOnError; | 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.TestSuite; | ||||
| import junit.framework.AssertionFailedError; | import junit.framework.AssertionFailedError; | ||||
| import java.lang.reflect.Method; | import java.lang.reflect.Method; | ||||
| import java.io.BufferedReader; | |||||
| import java.io.PrintStream; | import java.io.PrintStream; | ||||
| import java.io.PrintWriter; | |||||
| import java.io.StringReader; | |||||
| import java.io.StringWriter; | |||||
| import java.io.ByteArrayOutputStream; | import java.io.ByteArrayOutputStream; | ||||
| import java.io.IOException; | import java.io.IOException; | ||||
| import java.io.FileInputStream; | import java.io.FileInputStream; | ||||
| @@ -121,6 +125,24 @@ public class JUnitTestRunner implements TestListener { | |||||
| */ | */ | ||||
| private TestResult res; | 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. | * 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 | * Constructor for fork=true or when the user hasn't specified a | ||||
| * classpath. | * classpath. | ||||
| */ | */ | ||||
| public JUnitTestRunner(JUnitTest test, boolean haltOnError, | |||||
| public JUnitTestRunner(JUnitTest test, boolean haltOnError, boolean filtertrace, | |||||
| boolean haltOnFailure) { | boolean haltOnFailure) { | ||||
| this(test, haltOnError, haltOnFailure, null); | |||||
| this(test, haltOnError, filtertrace, haltOnFailure, null); | |||||
| } | } | ||||
| /** | /** | ||||
| * Constructor to use when the user has specified a classpath. | * 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) { | boolean haltOnFailure, ClassLoader loader) { | ||||
| //JUnitTestRunner.filtertrace = filtertrace; | |||||
| this.filtertrace = filtertrace; | |||||
| this.junitTest = test; | this.junitTest = test; | ||||
| this.haltOnError = haltOnError; | this.haltOnError = haltOnError; | ||||
| this.haltOnFailure = haltOnFailure; | this.haltOnFailure = haltOnFailure; | ||||
| @@ -378,6 +402,7 @@ public class JUnitTestRunner implements TestListener { | |||||
| boolean exitAtEnd = true; | boolean exitAtEnd = true; | ||||
| boolean haltError = false; | boolean haltError = false; | ||||
| boolean haltFail = false; | boolean haltFail = false; | ||||
| boolean stackfilter = true; | |||||
| Properties props = new Properties(); | Properties props = new Properties(); | ||||
| if (args.length == 0) { | if (args.length == 0) { | ||||
| @@ -390,6 +415,8 @@ public class JUnitTestRunner implements TestListener { | |||||
| haltError = Project.toBoolean(args[i].substring(12)); | haltError = Project.toBoolean(args[i].substring(12)); | ||||
| } else if (args[i].startsWith("haltOnFailure=")) { | } else if (args[i].startsWith("haltOnFailure=")) { | ||||
| haltFail = Project.toBoolean(args[i].substring(14)); | 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=")) { | } else if (args[i].startsWith("formatter=")) { | ||||
| try { | try { | ||||
| createAndStoreFormatter(args[i].substring(10)); | createAndStoreFormatter(args[i].substring(10)); | ||||
| @@ -405,7 +432,7 @@ public class JUnitTestRunner implements TestListener { | |||||
| } | } | ||||
| JUnitTest t = new JUnitTest(args[0]); | JUnitTest t = new JUnitTest(args[0]); | ||||
| // Add/overlay system properties on the properties from the Ant project | // Add/overlay system properties on the properties from the Ant project | ||||
| Hashtable p = System.getProperties(); | Hashtable p = System.getProperties(); | ||||
| for (Enumeration enum = p.keys(); enum.hasMoreElements(); ) { | for (Enumeration enum = p.keys(); enum.hasMoreElements(); ) { | ||||
| @@ -414,7 +441,7 @@ public class JUnitTestRunner implements TestListener { | |||||
| } | } | ||||
| t.setProperties(props); | t.setProperties(props); | ||||
| JUnitTestRunner runner = new JUnitTestRunner(t, haltError, haltFail); | |||||
| JUnitTestRunner runner = new JUnitTestRunner(t, haltError, stackfilter, haltFail); | |||||
| transferFormatters(runner); | transferFormatters(runner); | ||||
| runner.run(); | runner.run(); | ||||
| System.exit(runner.getRetCode()); | System.exit(runner.getRetCode()); | ||||
| @@ -443,5 +470,53 @@ public class JUnitTestRunner implements TestListener { | |||||
| } | } | ||||
| fromCmdLine.addElement(fe.createFormatter()); | 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 | } // JUnitTestRunner | ||||
| @@ -248,7 +248,8 @@ public class PlainJUnitResultFormatter implements JUnitResultFormatter { | |||||
| wri.println(type); | wri.println(type); | ||||
| wri.println(t.getMessage()); | wri.println(t.getMessage()); | ||||
| t.printStackTrace(wri); | |||||
| String strace = JUnitTestRunner.getFilteredTrace(t); | |||||
| wri.print(strace); | |||||
| wri.println(""); | wri.println(""); | ||||
| } | } | ||||
| } | } | ||||
| @@ -261,9 +261,8 @@ public class XMLJUnitResultFormatter implements JUnitResultFormatter, XMLConstan | |||||
| } | } | ||||
| nested.setAttribute(ATTR_TYPE, t.getClass().getName()); | 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); | nested.appendChild(trace); | ||||
| } | } | ||||