From a25bca9c14a1c802c1dec57771f11a77143e726c Mon Sep 17 00:00:00 2001 From: Stephane Bailliez Date: Fri, 11 Jan 2002 23:35:43 +0000 Subject: [PATCH] Making progress to collect JUnit tests. git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@270673 13f79535-47bb-0310-9956-ffa450edef68 --- .../optional/junit/BatchTestElement.java | 99 +++++++++ .../junit/ClasspathTestCollector.java | 176 ++++++++++++++++ .../taskdefs/optional/junit/JUnitTask.java | 42 +++- .../taskdefs/optional/junit/TestElement.java | 70 +++++++ .../taskdefs/optional/junit/ZipScanner.java | 193 ++++++++++++++++++ 5 files changed, 574 insertions(+), 6 deletions(-) create mode 100644 proposal/sandbox/junit/src/main/org/apache/tools/ant/taskdefs/optional/junit/BatchTestElement.java create mode 100644 proposal/sandbox/junit/src/main/org/apache/tools/ant/taskdefs/optional/junit/ClasspathTestCollector.java create mode 100644 proposal/sandbox/junit/src/main/org/apache/tools/ant/taskdefs/optional/junit/TestElement.java create mode 100644 proposal/sandbox/junit/src/main/org/apache/tools/ant/taskdefs/optional/junit/ZipScanner.java diff --git a/proposal/sandbox/junit/src/main/org/apache/tools/ant/taskdefs/optional/junit/BatchTestElement.java b/proposal/sandbox/junit/src/main/org/apache/tools/ant/taskdefs/optional/junit/BatchTestElement.java new file mode 100644 index 000000000..1acde5759 --- /dev/null +++ b/proposal/sandbox/junit/src/main/org/apache/tools/ant/taskdefs/optional/junit/BatchTestElement.java @@ -0,0 +1,99 @@ +/* + * The Apache Software License, Version 1.1 + * + * Copyright (c) 2002 The Apache Software Foundation. All rights + * reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Apache Software Foundation (http://www.apache.org/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "The Jakarta Project", "Ant", and "Apache Software + * Foundation" must not be used to endorse or promote products derived + * from this software without prior written permission. For written + * permission, please contact apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + */ +package org.apache.tools.ant.taskdefs.optional.junit; + +import java.util.Enumeration; + +import junit.runner.TestCollector; + +import org.apache.tools.ant.types.Path; +import org.apache.tools.ant.types.PatternSet; + +/** + * A test element where tests files are specified by include/exclude + * patterns. tests files location are specified by one ore multiple + * path elements. (directory or archive). + * + *
+ * 
+ * 
+ * 
+ * + * @author Stephane Bailliez + */ +public class BatchTestElement implements TestCollector { + + private ClasspathTestCollector collector = new ClasspathTestCollector(); + +// Test collector implementation + + public Enumeration collectTests() { + return collector.collectTests(); + } + +// Ant bean accessors + + public void setPath(Path path) { + collector.setPath(path); + } + + public PatternSet.NameEntry createInclude() { + return collector.createInclude(); + } + + public PatternSet.NameEntry createExclude() { + return collector.createExclude(); + } + +} diff --git a/proposal/sandbox/junit/src/main/org/apache/tools/ant/taskdefs/optional/junit/ClasspathTestCollector.java b/proposal/sandbox/junit/src/main/org/apache/tools/ant/taskdefs/optional/junit/ClasspathTestCollector.java new file mode 100644 index 000000000..547c2faa7 --- /dev/null +++ b/proposal/sandbox/junit/src/main/org/apache/tools/ant/taskdefs/optional/junit/ClasspathTestCollector.java @@ -0,0 +1,176 @@ +/* + * The Apache Software License, Version 1.1 + * + * Copyright (c) 2001 The Apache Software Foundation. All rights + * reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Apache Software Foundation (http://www.apache.org/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "The Jakarta Project", "Ant", and "Apache Software + * Foundation" must not be used to endorse or promote products derived + * from this software without prior written permission. For written + * permission, please contact apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + */ +package org.apache.tools.ant.taskdefs.optional.junit; + +import java.util.Enumeration; +import java.util.Vector; +import java.util.Hashtable; +import java.util.zip.ZipFile; +import java.util.zip.ZipEntry; +import java.io.File; + +import junit.runner.TestCollector; + +import org.apache.tools.ant.types.PatternSet; +import org.apache.tools.ant.types.Path; +import org.apache.tools.ant.ProjectComponent; +import org.apache.tools.ant.DirectoryScanner; +import org.apache.tools.ant.Project; + +/** + * A rough implementation of a test collector that will collect tests + * using include/exclude patterns in a set of paths. A path can either + * be a directory or an archive. (zip or jar file) + * + * @author Stephane Bailliez + */ +public class ClasspathTestCollector extends ProjectComponent + implements TestCollector { + + private final static int SUFFIX_LENGTH= ".class".length(); + + private PatternSet patterns = new PatternSet(); + + private Path path = null; + + public Enumeration collectTests() { + Hashtable collected = new Hashtable(); + // start from last, so that first elements + // override last one in case there are duplicates. + // ie mimic classpath behavior. + String[] paths = path.list(); + for (int i = paths.length; i >= 0; i--){ + File f = new File(paths[i]); + Vector included = null; + if ( f.isDirectory() ){ + included = gatherFromDirectory(f); + } else if ( f.getName().endsWith(".zip") + || f.getName().endsWith(".jar") ) { + included = gatherFromArchive(f); + } else { + continue; + } + // add tests to the already collected one + final int includedCount = included.size(); + for (int j = 0; j < includedCount; j++){ + String testname = (String)included.elementAt(i); + collected.put(testname, ""); + } + } + return collected.keys(); + } + + + protected Vector gatherFromDirectory(File dir){ + Project project = getProject(); + DirectoryScanner ds = new DirectoryScanner(); + ds.setBasedir(dir); + ds.setIncludes(patterns.getIncludePatterns(project)); + ds.setExcludes(patterns.getExcludePatterns(project)); + ds.scan(); + String[] included = ds.getIncludedFiles(); + return testClassNameFromFile(included); + } + + protected Vector gatherFromArchive(File zip){ + ZipScanner zs = new ZipScanner(); + zs.setBasedir(zip); + zs.setIncludes(patterns.getIncludePatterns(project)); + zs.setExcludes(patterns.getExcludePatterns(project)); + zs.scan(); + String[] included = zs.getIncludedFiles(); + return testClassNameFromFile(included); + } + + protected Vector testClassNameFromFile(String[] classFileNames){ + Vector tests = new Vector(classFileNames.length); + for (int i = 0; i < classFileNames.length; i++){ + String file = classFileNames[i]; + if ( isTestClass(file) ){ + String classname = classNameFromFile(file); + tests.addElement(classname); + } + } + return tests; + } + + protected boolean isTestClass(String classFileName) { + return classFileName.endsWith(".class"); + } + + protected String classNameFromFile(String classFileName) { + // convert /a/b.class to a.b + String s= classFileName.substring(0, classFileName.length()-SUFFIX_LENGTH); + String s2= s.replace(File.separatorChar, '.'); + if ( s2.startsWith(".") ){ + s2 = s2.substring(1); + } + return s2; + } + +// Ant bean accessors + + public void setPath(Path path){ + this.path = path; + } + + public PatternSet.NameEntry createInclude(){ + return patterns.createInclude(); + } + + public PatternSet.NameEntry createExclude(){ + return patterns.createExclude(); + } + +} diff --git a/proposal/sandbox/junit/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTask.java b/proposal/sandbox/junit/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTask.java index f2faa6969..ccf7d16ff 100644 --- a/proposal/sandbox/junit/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTask.java +++ b/proposal/sandbox/junit/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTask.java @@ -60,6 +60,9 @@ import java.io.IOException; import java.io.OutputStream; import java.util.Properties; import java.util.Vector; +import java.util.Enumeration; + +import junit.runner.TestCollector; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.Project; @@ -73,6 +76,7 @@ import org.apache.tools.ant.types.Path; import org.apache.tools.ant.util.FileUtils; /** + * The core JUnit task. * * @author Stephane Bailliez */ @@ -87,6 +91,9 @@ public class JUnitTask extends Task { /** formatters that write the tests results */ private Vector formatters = new Vector(); + /** test collector elements */ + private Vector testCollectors = new Vector(); + /** stop the test run if a failure occurs */ private boolean haltOnFailure = false; @@ -126,13 +133,14 @@ public class JUnitTask extends Task { props.setProperty("debug", "true"); props.setProperty("host", "127.0.0.1"); props.setProperty("port", String.valueOf(port)); - StringBuffer classnames = new StringBuffer(); - //@fixme get all test classes to run... - final int testcount = 0; - for (int i = 0; i < testcount; i++) { - classnames.append("").append("\n"); + // get all test classes to run... + StringBuffer buf = new StringBuffer(10240); + Enumeration classnames = collectTests(); + while ( classnames.hasMoreElements() ){ + String classname = (String)classnames.nextElement(); + buf.append(classname).append(" "); } - props.setProperty("classnames", classnames.toString()); + props.setProperty("classnames", buf.toString()); // dump the properties to a temporary file. FileUtils futils = FileUtils.newFileUtils(); @@ -159,6 +167,18 @@ public class JUnitTask extends Task { return f; } + /** + * @return all collected tests specified with test elements. + */ + protected Enumeration collectTests(){ + Enumeration[] tests = new Enumeration[testCollectors.size()]; + for (int i = 0; i < testCollectors.size(); i++){ + TestCollector te = (TestCollector)testCollectors.elementAt(i); + tests[i] = te.collectTests(); + } + return Enumerations.fromCompound(tests); + } + // Ant bean accessors public void setPort(int port) { @@ -183,6 +203,16 @@ public class JUnitTask extends Task { this.formatters.addElement(f); } + /** add a single test element */ + public void addTest(TestElement te) { + this.testCollectors.addElement(te); + } + + /** add a batch test element */ + public void addBatchTest(BatchTestElement bte) { + this.testCollectors.addElement(bte); + } + /** * Set the maximum memory to be used by the TestRunner * @param max the value as defined by -mx or -Xmx diff --git a/proposal/sandbox/junit/src/main/org/apache/tools/ant/taskdefs/optional/junit/TestElement.java b/proposal/sandbox/junit/src/main/org/apache/tools/ant/taskdefs/optional/junit/TestElement.java new file mode 100644 index 000000000..b8271be89 --- /dev/null +++ b/proposal/sandbox/junit/src/main/org/apache/tools/ant/taskdefs/optional/junit/TestElement.java @@ -0,0 +1,70 @@ +/* + * The Apache Software License, Version 1.1 + * + * Copyright (c) 2002 The Apache Software Foundation. All rights + * reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Apache Software Foundation (http://www.apache.org/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "The Jakarta Project", "Ant", and "Apache Software + * Foundation" must not be used to endorse or promote products derived + * from this software without prior written permission. For written + * permission, please contact apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + */ +package org.apache.tools.ant.taskdefs.optional.junit; + +import java.util.Enumeration; + +import junit.runner.TestCollector; + +/** + * A simple test element. + * + * @author Stephane Bailliez + */ +public class TestElement implements TestCollector { + + public Enumeration collectTests() { + return null; + } +} diff --git a/proposal/sandbox/junit/src/main/org/apache/tools/ant/taskdefs/optional/junit/ZipScanner.java b/proposal/sandbox/junit/src/main/org/apache/tools/ant/taskdefs/optional/junit/ZipScanner.java new file mode 100644 index 000000000..8d1836b18 --- /dev/null +++ b/proposal/sandbox/junit/src/main/org/apache/tools/ant/taskdefs/optional/junit/ZipScanner.java @@ -0,0 +1,193 @@ +/* + * The Apache Software License, Version 1.1 + * + * Copyright (c) 2001 The Apache Software Foundation. All rights + * reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Apache Software Foundation (http://www.apache.org/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "The Jakarta Project", "Ant", and "Apache Software + * Foundation" must not be used to endorse or promote products derived + * from this software without prior written permission. For written + * permission, please contact apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + */ +package org.apache.tools.ant.taskdefs.optional.junit; + +import java.io.File; +import java.io.IOException; +import java.util.Vector; +import java.util.Enumeration; +import java.util.zip.ZipFile; +import java.util.zip.ZipEntry; + +import org.apache.tools.ant.DirectoryScanner; +import org.apache.tools.ant.FileScanner; +import org.apache.tools.ant.BuildException; + +/** + * Provide a way to scan entries in a zip file. Note that it extends + * DirectoryScanner to make use of protected methods but implementation + * may not be valid for some methods. + *

+ * the setBaseDir() must be called to set the reference to the archive + * file (.jar or .zip). + *

+ * + * @author Stephane Bailliez + */ +public class ZipScanner extends DirectoryScanner { + public ZipScanner() { + } + + public void setExcludes(String[] excludes) { + super.setExcludes(excludes); + normalize(this.excludes); + } + + public void setIncludes(String[] includes) { + super.setIncludes(includes); + normalize(this.includes); + } + + /** + * normalize a set of paths so that it uses / otherwise matching will + * fail beautifully since archives use / to denote a path. + */ + protected void normalize(String[] files){ + if (files != null){ + for (int i = 0; i < files.length; i++){ + files[i] = files[i].replace('\\','/'); + } + } + } + + /** + * Scans the archive for files that match at least one include + * pattern, and don't match any exclude patterns. + * + * @exception IllegalStateException when the zip file was set incorrecly + */ + public void scan() { + if (basedir == null) { + throw new IllegalStateException("No zipfile set"); + } + if (!basedir.exists()) { + throw new IllegalStateException("zipfile " + basedir + + " does not exist"); + } + if (basedir.isDirectory()) { + throw new IllegalStateException("zipfile " + basedir + + " is not a file"); + } + + if (includes == null) { + // No includes supplied, so set it to 'matches all' + includes = new String[1]; + includes[0] = "**"; + } + if (excludes == null) { + excludes = new String[0]; + } + + filesIncluded = new Vector(); + filesNotIncluded = new Vector(); + filesExcluded = new Vector(); + dirsIncluded = new Vector(); + dirsNotIncluded = new Vector(); + dirsExcluded = new Vector(); + + if (isIncluded("")) { + if (!isExcluded("")) { + dirsIncluded.addElement(""); + } else { + dirsExcluded.addElement(""); + } + } else { + dirsNotIncluded.addElement(""); + } + scandir(basedir, "", true); + } + + protected void scandir(File file, String vpath, boolean fast) { + ZipFile zip = null; + try { + zip = new ZipFile(file); + } catch (IOException e){ + throw new IllegalStateException(e.getMessage()); + } + + Enumeration entries = zip.entries(); + while ( entries.hasMoreElements() ) { + ZipEntry entry = (ZipEntry)entries.nextElement(); + String name = entry.getName(); + // @fixme do we need to strip out entries that starts + // with . or ./ ? + if (entry.isDirectory()) { + if (isIncluded(name)) { + if (!isExcluded(name)) { + dirsIncluded.addElement(name); + } else { + everythingIncluded = false; + dirsExcluded.addElement(name); + } + } else { + everythingIncluded = false; + dirsNotIncluded.addElement(name); + } + } else { + if (isIncluded(name)) { + if (!isExcluded(name)) { + filesIncluded.addElement(name); + } else { + everythingIncluded = false; + filesExcluded.addElement(name); + } + } else { + everythingIncluded = false; + filesNotIncluded.addElement(name); + } + } + } + } + +}