Submitted by: Stephane Bailliez <sbailliez@imediation.com> git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@268557 13f79535-47bb-0310-9956-ffa450edef68master
| @@ -69,75 +69,140 @@ import java.io.File; | |||
| * @author <a href="mailto:jeff.martin@synamic.co.uk">Jeff Martin</a> | |||
| * @author <a href="mailto:stefan.bodewig@epost.de">Stefan Bodewig</a> | |||
| * @author <a href="mailto:sbailliez@imediation.com">Stephane Bailliez</a> | |||
| * | |||
| * @see JUnitTest | |||
| */ | |||
| public final class BatchTest extends BaseTest { | |||
| /** the reference to the project */ | |||
| private Project project; | |||
| /** the list of filesets containing the testcase filename rules */ | |||
| private Vector filesets = new Vector(); | |||
| /** | |||
| * create a new batchtest instance | |||
| * @param project the project it depends on. | |||
| */ | |||
| public BatchTest(Project project){ | |||
| this.project = project; | |||
| } | |||
| /** | |||
| * Add a new fileset instance to this batchtest. Whatever the fileset is, | |||
| * only filename that are <tt>.java</tt> or <tt>.class</tt> will be | |||
| * considered as 'candidates'. | |||
| * @param fs the new fileset containing the rules to get the testcases. | |||
| */ | |||
| public void addFileSet(FileSet fs) { | |||
| filesets.addElement(fs); | |||
| } | |||
| /** | |||
| * return all <tt>JUnitTest</tt> instances obtain by applying the fileset rules. | |||
| * @return an enumeration of all elements of this batchtest that are | |||
| * a <tt>JUnitTest</tt> instance. | |||
| * @see addTestsTo(Vector) | |||
| */ | |||
| public final Enumeration elements(){ | |||
| return new FileList(); | |||
| JUnitTest[] tests = createAllJUnitTest(); | |||
| return Enumerations.fromArray(tests); | |||
| } | |||
| public class FileList implements Enumeration{ | |||
| private String files[]=null; | |||
| private int i=0; | |||
| private FileList(){ | |||
| Vector v = new Vector(); | |||
| for (int j=0; j<filesets.size(); j++) { | |||
| FileSet fs = (FileSet) filesets.elementAt(j); | |||
| DirectoryScanner ds = fs.getDirectoryScanner(project); | |||
| ds.scan(); | |||
| String[] f = ds.getIncludedFiles(); | |||
| for (int k=0; k<f.length; k++) { | |||
| if (f[k].endsWith(".java")) { | |||
| v.addElement(f[k].substring(0, f[k].length()-5)); | |||
| } else if (f[k].endsWith(".class")) { | |||
| v.addElement(f[k].substring(0, f[k].length()-6)); | |||
| } | |||
| } | |||
| } | |||
| files = new String[v.size()]; | |||
| v.copyInto(files); | |||
| /** | |||
| * Convenient method to merge the <tt>JUnitTest</tt>s of this batchtest | |||
| * to a <tt>Vector</tt>. | |||
| * @param v the vector to which should be added all individual tests of this | |||
| * batch test. | |||
| */ | |||
| final void addTestsTo(Vector v){ | |||
| JUnitTest[] tests = createAllJUnitTest(); | |||
| v.ensureCapacity( v.size() + tests.length); | |||
| for (int i = 0; i < tests.length; i++) { | |||
| v.addElement(tests[i]); | |||
| } | |||
| } | |||
| public final boolean hasMoreElements(){ | |||
| if(i<files.length)return true; | |||
| return false; | |||
| /** | |||
| * Create all <tt>JUnitTest</tt>s based on the filesets. Each instance | |||
| * is configured to match this instance properties. | |||
| * @return the array of all <tt>JUnitTest</tt>s that belongs to this batch. | |||
| */ | |||
| private JUnitTest[] createAllJUnitTest(){ | |||
| String[] filenames = getFilenames(); | |||
| JUnitTest[] tests = new JUnitTest[filenames.length]; | |||
| for (int i = 0; i < tests.length; i++) { | |||
| String classname = javaToClass(filenames[i]); | |||
| tests[i] = createJUnitTest(classname); | |||
| } | |||
| return tests; | |||
| } | |||
| public final Object nextElement() throws NoSuchElementException{ | |||
| if(hasMoreElements()){ | |||
| JUnitTest test = new JUnitTest(javaToClass(files[i])); | |||
| test.setHaltonerror(haltOnError); | |||
| test.setHaltonfailure(haltOnFail); | |||
| test.setFork(fork); | |||
| test.setIf(ifProperty); | |||
| test.setUnless(unlessProperty); | |||
| test.setTodir(destDir); | |||
| Enumeration list = formatters.elements(); | |||
| while (list.hasMoreElements()) { | |||
| test.addFormatter((FormatterElement)list.nextElement()); | |||
| /** | |||
| * Iterate over all filesets and return the filename of all files | |||
| * that end with <tt>.java</tt> or <tt>.class</tt>. This is to avoid | |||
| * wrapping a <tt>JUnitTest</tt> over an xml file for example. A Testcase | |||
| * is obviouslly a java file (compiled or not). | |||
| * @return an array of filenames without their extension. As they should | |||
| * normally be taken from their root, filenames should match their fully | |||
| * qualified class name (If it is not the case it will fail when running the test). | |||
| * For the class <tt>org/apache/Whatever.class</tt> it will return <tt>org/apache/Whatever</tt>. | |||
| */ | |||
| private String[] getFilenames(){ | |||
| Vector v = new Vector(); | |||
| final int size = this.filesets.size(); | |||
| for (int j=0; j<size; j++) { | |||
| FileSet fs = (FileSet) filesets.elementAt(j); | |||
| DirectoryScanner ds = fs.getDirectoryScanner(project); | |||
| ds.scan(); | |||
| String[] f = ds.getIncludedFiles(); | |||
| for (int k = 0; k < f.length; k++) { | |||
| String pathname = f[k]; | |||
| if (pathname.endsWith(".java")) { | |||
| v.addElement(pathname.substring(0, pathname.length()-".java".length())); | |||
| } else if (pathname.endsWith(".class")) { | |||
| v.addElement(pathname.substring(0, pathname.length()-".class".length())); | |||
| } | |||
| i++; | |||
| return test; | |||
| } | |||
| throw new NoSuchElementException(); | |||
| } | |||
| public final String javaToClass(String fileName){ | |||
| return fileName.replace(java.io.File.separatorChar, '.'); | |||
| String[] files = new String[v.size()]; | |||
| v.copyInto(files); | |||
| return files; | |||
| } | |||
| /** | |||
| * convenient method to convert a pathname without extension to a | |||
| * fully qualified classname. For example <tt>org/apache/Whatever</tt> will | |||
| * be converted to <tt>org.apache.Whatever</tt> | |||
| * @param filename the filename to "convert" to a classname. | |||
| * @return the classname matching the filename. | |||
| */ | |||
| public final static String javaToClass(String filename){ | |||
| return filename.replace(File.separatorChar, '.'); | |||
| } | |||
| /** | |||
| * Create a <tt>JUnitTest</tt> that has the same property as this | |||
| * <tt>BatchTest</tt> instance. | |||
| * @param classname the name of the class that should be run as a | |||
| * <tt>JUnitTest</tt>. It must be a fully qualified name. | |||
| * @return the <tt>JUnitTest</tt> over the given classname. | |||
| */ | |||
| private JUnitTest createJUnitTest(String classname){ | |||
| JUnitTest test = new JUnitTest(); | |||
| test.setName(classname); | |||
| test.setHaltonerror(this.haltOnError); | |||
| test.setHaltonfailure(this.haltOnFail); | |||
| test.setFork(this.fork); | |||
| test.setIf(this.ifProperty); | |||
| test.setUnless(this.unlessProperty); | |||
| test.setTodir(this.destDir); | |||
| Enumeration list = this.formatters.elements(); | |||
| while (list.hasMoreElements()) { | |||
| test.addFormatter((FormatterElement)list.nextElement()); | |||
| } | |||
| return test; | |||
| } | |||
| } | |||
| @@ -64,29 +64,29 @@ import java.util.NoSuchElementException; | |||
| * @author <a href="mailto:sbailliez@imediation.com">Stephane Bailliez</a> | |||
| */ | |||
| public final class Enumerations { | |||
| private Enumerations(){ | |||
| } | |||
| private Enumerations(){ | |||
| } | |||
| /** | |||
| * creates an enumeration from an array of objects. | |||
| * @param array the array of object to enumerate. | |||
| * @return the enumeration over the array of objects. | |||
| */ | |||
| public static Enumeration fromArray(Object[] array){ | |||
| return new ArrayEnumeration(array); | |||
| } | |||
| /** | |||
| * creates an enumeration from an array of objects. | |||
| * @param array the array of object to enumerate. | |||
| * @return the enumeration over the array of objects. | |||
| */ | |||
| public static Enumeration fromArray(Object[] array){ | |||
| return new ArrayEnumeration(array); | |||
| } | |||
| /** | |||
| * creates an enumeration from an array of enumeration. The created enumeration | |||
| * will sequentially enumerate over all elements of each enumeration and skip | |||
| * <tt>null</tt> enumeration elements in the array. | |||
| * @param enums the array of enumerations. | |||
| * @return the enumeration over the array of enumerations. | |||
| */ | |||
| public static Enumeration fromCompound(Enumeration[] enums){ | |||
| return new CompoundEnumeration(enums); | |||
| } | |||
| /** | |||
| * creates an enumeration from an array of enumeration. The created enumeration | |||
| * will sequentially enumerate over all elements of each enumeration and skip | |||
| * <tt>null</tt> enumeration elements in the array. | |||
| * @param enums the array of enumerations. | |||
| * @return the enumeration over the array of enumerations. | |||
| */ | |||
| public static Enumeration fromCompound(Enumeration[] enums){ | |||
| return new CompoundEnumeration(enums); | |||
| } | |||
| } | |||
| @@ -96,47 +96,47 @@ public final class Enumerations { | |||
| * @author <a href="mailto:sbailliez@imediation.com">Stephane Bailliez</a> | |||
| */ | |||
| class ArrayEnumeration implements Enumeration { | |||
| /** object array */ | |||
| private Object[] array; | |||
| /** current index */ | |||
| private int pos; | |||
| /** | |||
| * Initialize a new enumeration that wraps an array. | |||
| * @param array the array of object to enumerate. | |||
| */ | |||
| public ArrayEnumeration(Object[] array){ | |||
| this.array = array; | |||
| this.pos = 0; | |||
| } | |||
| /** | |||
| * Tests if this enumeration contains more elements. | |||
| * | |||
| * @return <code>true</code> if and only if this enumeration object | |||
| * contains at least one more element to provide; | |||
| * <code>false</code> otherwise. | |||
| */ | |||
| public boolean hasMoreElements() { | |||
| return (pos < array.length); | |||
| } | |||
| /** object array */ | |||
| private Object[] array; | |||
| /** current index */ | |||
| private int pos; | |||
| /** | |||
| * Initialize a new enumeration that wraps an array. | |||
| * @param array the array of object to enumerate. | |||
| */ | |||
| public ArrayEnumeration(Object[] array){ | |||
| this.array = array; | |||
| this.pos = 0; | |||
| } | |||
| /** | |||
| * Tests if this enumeration contains more elements. | |||
| * | |||
| * @return <code>true</code> if and only if this enumeration object | |||
| * contains at least one more element to provide; | |||
| * <code>false</code> otherwise. | |||
| */ | |||
| public boolean hasMoreElements() { | |||
| return (pos < array.length); | |||
| } | |||
| /** | |||
| * Returns the next element of this enumeration if this enumeration | |||
| * object has at least one more element to provide. | |||
| * | |||
| * @return the next element of this enumeration. | |||
| * @throws NoSuchElementException if no more elements exist. | |||
| */ | |||
| public Object nextElement() throws NoSuchElementException { | |||
| if (hasMoreElements()) { | |||
| Object o = array[pos]; | |||
| pos++; | |||
| return o; | |||
| } | |||
| throw new NoSuchElementException(); | |||
| } | |||
| /** | |||
| * Returns the next element of this enumeration if this enumeration | |||
| * object has at least one more element to provide. | |||
| * | |||
| * @return the next element of this enumeration. | |||
| * @throws NoSuchElementException if no more elements exist. | |||
| */ | |||
| public Object nextElement() throws NoSuchElementException { | |||
| if (hasMoreElements()) { | |||
| Object o = array[pos]; | |||
| pos++; | |||
| return o; | |||
| } | |||
| throw new NoSuchElementException(); | |||
| } | |||
| } | |||
| /** | |||
| * Convenient enumeration over an array of enumeration. For example: | |||
| @@ -170,46 +170,46 @@ class ArrayEnumeration implements Enumeration { | |||
| * @author <a href="mailto:sbailliez@imediation.com">Stephane Bailliez</a> | |||
| */ | |||
| class CompoundEnumeration implements Enumeration { | |||
| /** enumeration array */ | |||
| private Enumeration[] enumArray; | |||
| /** index in the enums array */ | |||
| private int index = 0; | |||
| /** enumeration array */ | |||
| private Enumeration[] enumArray; | |||
| /** index in the enums array */ | |||
| private int index = 0; | |||
| public CompoundEnumeration(Enumeration[] enumarray) { | |||
| this.enumArray = enumarray; | |||
| this.enumArray = enumarray; | |||
| } | |||
| /** | |||
| * Tests if this enumeration contains more elements. | |||
| * | |||
| * @return <code>true</code> if and only if this enumeration object | |||
| * contains at least one more element to provide; | |||
| * <code>false</code> otherwise. | |||
| */ | |||
| /** | |||
| * Tests if this enumeration contains more elements. | |||
| * | |||
| * @return <code>true</code> if and only if this enumeration object | |||
| * contains at least one more element to provide; | |||
| * <code>false</code> otherwise. | |||
| */ | |||
| public boolean hasMoreElements() { | |||
| while (index < enumArray.length) { | |||
| if (enumArray[index] != null && enumArray[index].hasMoreElements()) { | |||
| return true; | |||
| } | |||
| index++; | |||
| } | |||
| return false; | |||
| while (index < enumArray.length) { | |||
| if (enumArray[index] != null && enumArray[index].hasMoreElements()) { | |||
| return true; | |||
| } | |||
| index++; | |||
| } | |||
| return false; | |||
| } | |||
| /** | |||
| * Returns the next element of this enumeration if this enumeration | |||
| * object has at least one more element to provide. | |||
| * | |||
| * @return the next element of this enumeration. | |||
| * @throws NoSuchElementException if no more elements exist. | |||
| */ | |||
| /** | |||
| * Returns the next element of this enumeration if this enumeration | |||
| * object has at least one more element to provide. | |||
| * | |||
| * @return the next element of this enumeration. | |||
| * @throws NoSuchElementException if no more elements exist. | |||
| */ | |||
| public Object nextElement() throws NoSuchElementException { | |||
| if ( hasMoreElements() ) { | |||
| return enumArray[index].nextElement(); | |||
| } | |||
| throw new NoSuchElementException(); | |||
| if ( hasMoreElements() ) { | |||
| return enumArray[index].nextElement(); | |||
| } | |||
| throw new NoSuchElementException(); | |||
| } | |||
| } | |||
| @@ -213,10 +213,7 @@ public class JUnitTask extends Task { | |||
| public void addSysproperty(Environment.Variable sysp) { | |||
| commandline.addSysproperty(sysp); | |||
| } | |||
| /** | |||
| * create a classpath to use for forked jvm | |||
| */ | |||
| public Path createClasspath() { | |||
| return commandline.createClasspath(project).createPath(); | |||
| } | |||
| @@ -260,21 +257,17 @@ public class JUnitTask extends Task { | |||
| */ | |||
| public void execute() throws BuildException { | |||
| Enumeration list = getIndividualTests(); | |||
| try { | |||
| while (list.hasMoreElements()) { | |||
| JUnitTest test = (JUnitTest)list.nextElement(); | |||
| if ( test.shouldRun(project)) { | |||
| execute(test); | |||
| } | |||
| while (list.hasMoreElements()) { | |||
| JUnitTest test = (JUnitTest)list.nextElement(); | |||
| if ( test.shouldRun(project)) { | |||
| execute(test); | |||
| } | |||
| } finally { | |||
| //@todo here we should run test aggregation (SBa) | |||
| } | |||
| } | |||
| protected void execute(JUnitTest test) throws BuildException { | |||
| // set the default values if not specified | |||
| //@todo should be moved to the test class (?) (SBa) | |||
| //@todo should be moved to the test class instead. | |||
| if (test.getTodir() == null) { | |||
| test.setTodir(project.resolveFile(".")); | |||
| } | |||
| @@ -288,7 +281,7 @@ public class JUnitTask extends Task { | |||
| boolean wasKilled = false; | |||
| if (!test.getFork()) { | |||
| exitValue = executeInVM(test); | |||
| } else { | |||
| } else { | |||
| ExecuteWatchdog watchdog = createWatchdog(); | |||
| exitValue = executeAsForked(test, watchdog); | |||
| // null watchdog means no timeout, you'd better not check with null | |||
| @@ -459,10 +452,9 @@ public class JUnitTask extends Task { | |||
| private FormatterElement[] mergeFormatters(JUnitTest test){ | |||
| Vector feVector = (Vector)formatters.clone(); | |||
| FormatterElement[] fes = test.getFormatters(); | |||
| FormatterElement[] feArray = new FormatterElement[feVector.size() + fes.length]; | |||
| test.addFormattersTo(feVector); | |||
| FormatterElement[] feArray = new FormatterElement[feVector.size()]; | |||
| feVector.copyInto(feArray); | |||
| System.arraycopy(fes, 0, feArray, feVector.size(), fes.length); | |||
| return feArray; | |||
| } | |||
| @@ -73,7 +73,10 @@ public class JUnitTest extends BaseTest { | |||
| /** the name of the result file */ | |||
| private String outfile = null; | |||
| // @todo this is duplicating TestResult information. Only the time is not | |||
| // part of the result. So we'd better derive a new class from TestResult | |||
| // and deal with it. (SB) | |||
| private long runs, failures, errors; | |||
| private long runTime; | |||
| @@ -139,4 +142,14 @@ public class JUnitTest extends BaseTest { | |||
| formatters.copyInto(fes); | |||
| return fes; | |||
| } | |||
| /** | |||
| * Convenient method to add formatters to a vector | |||
| */ | |||
| void addFormattersTo(Vector v){ | |||
| final int count = formatters.size(); | |||
| for (int i = 0; i < count; i++){ | |||
| v.addElement( formatters.elementAt(i) ); | |||
| } | |||
| } | |||
| } | |||
| @@ -74,7 +74,7 @@ import junit.framework.TestCase; | |||
| * @author <a href="mailto:stefan.bodewig@epost.de">Stefan Bodewig</a> | |||
| */ | |||
| public class XMLJUnitResultFormatter implements JUnitResultFormatter { | |||
| public class XMLJUnitResultFormatter implements JUnitResultFormatter, XMLConstants { | |||
| private static DocumentBuilder getDocumentBuilder() { | |||
| try { | |||
| @@ -88,7 +88,8 @@ public class XMLJUnitResultFormatter implements JUnitResultFormatter { | |||
| /** | |||
| * Formatter for timings. | |||
| */ | |||
| private NumberFormat nf = NumberFormat.getInstance(); | |||
| private NumberFormat nf = NumberFormat.getInstance(Locale.US); | |||
| /** | |||
| * The XML document. | |||
| */ | |||
| @@ -121,19 +122,19 @@ public class XMLJUnitResultFormatter implements JUnitResultFormatter { | |||
| */ | |||
| public void startTestSuite(JUnitTest suite) { | |||
| doc = getDocumentBuilder().newDocument(); | |||
| rootElement = doc.createElement("testsuite"); | |||
| rootElement.setAttribute("name", suite.getName()); | |||
| rootElement = doc.createElement(TESTSUITE); | |||
| rootElement.setAttribute(ATTR_NAME, suite.getName()); | |||
| } | |||
| /** | |||
| * The whole testsuite ended. | |||
| */ | |||
| public void endTestSuite(JUnitTest suite) throws BuildException { | |||
| rootElement.setAttribute("tests", ""+suite.runCount()); | |||
| rootElement.setAttribute("failures", ""+suite.failureCount()); | |||
| rootElement.setAttribute("errors", ""+suite.errorCount()); | |||
| rootElement.setAttribute("time", | |||
| nf.format(suite.getRunTime()/1000.0)+" sec"); | |||
| rootElement.setAttribute(ATTR_TESTS, ""+suite.runCount()); | |||
| rootElement.setAttribute(ATTR_FAILURES, ""+suite.failureCount()); | |||
| rootElement.setAttribute(ATTR_ERRORS, ""+suite.errorCount()); | |||
| rootElement.setAttribute(ATTR_TIME, | |||
| nf.format(suite.getRunTime()/1000.0)); | |||
| if (out != null) { | |||
| Writer wri = null; | |||
| try { | |||
| @@ -162,8 +163,8 @@ public class XMLJUnitResultFormatter implements JUnitResultFormatter { | |||
| */ | |||
| public void startTest(Test t) { | |||
| lastTestStart = System.currentTimeMillis(); | |||
| currentTest = doc.createElement("testcase"); | |||
| currentTest.setAttribute("name", ((TestCase) t).name()); | |||
| currentTest = doc.createElement(TESTCASE); | |||
| currentTest.setAttribute(ATTR_NAME, ((TestCase) t).name()); | |||
| rootElement.appendChild(currentTest); | |||
| } | |||
| @@ -173,7 +174,7 @@ public class XMLJUnitResultFormatter implements JUnitResultFormatter { | |||
| * <p>A Test is finished. | |||
| */ | |||
| public void endTest(Test test) { | |||
| currentTest.setAttribute("time", | |||
| currentTest.setAttribute(ATTR_TIME, | |||
| nf.format((System.currentTimeMillis()-lastTestStart) | |||
| / 1000.0)); | |||
| } | |||
| @@ -184,7 +185,7 @@ public class XMLJUnitResultFormatter implements JUnitResultFormatter { | |||
| * <p>A Test failed. | |||
| */ | |||
| public void addFailure(Test test, Throwable t) { | |||
| formatError("failure", test, t); | |||
| formatError(FAILURE, test, t); | |||
| } | |||
| /** | |||
| @@ -202,7 +203,7 @@ public class XMLJUnitResultFormatter implements JUnitResultFormatter { | |||
| * <p>An error occured while running the test. | |||
| */ | |||
| public void addError(Test test, Throwable t) { | |||
| formatError("error", test, t); | |||
| formatError(ERROR, test, t); | |||
| } | |||
| private void formatError(String type, Test test, Throwable t) { | |||
| @@ -219,9 +220,9 @@ public class XMLJUnitResultFormatter implements JUnitResultFormatter { | |||
| String message = t.getMessage(); | |||
| if (message != null && message.length() > 0) { | |||
| nested.setAttribute("message", t.getMessage()); | |||
| nested.setAttribute(ATTR_MESSAGE, t.getMessage()); | |||
| } | |||
| nested.setAttribute("type", t.getClass().getName()); | |||
| nested.setAttribute(ATTR_TYPE, t.getClass().getName()); | |||
| StringWriter swr = new StringWriter(); | |||
| t.printStackTrace(new PrintWriter(swr, true)); | |||