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)); | |||