diff options
-rw-r--r-- | Common.mk | 8 | ||||
-rw-r--r-- | src/junit/runner/ClassPathTestCollector.java | 81 | ||||
-rw-r--r-- | src/junit/runner/FailureDetailView.java | 28 | ||||
-rw-r--r-- | src/junit/runner/LoadingTestCollector.java | 69 | ||||
-rw-r--r-- | src/junit/runner/ReloadingTestSuiteLoader.java | 20 | ||||
-rw-r--r-- | src/junit/runner/SimpleTestCollector.java | 21 | ||||
-rw-r--r-- | src/junit/runner/Sorter.java | 37 | ||||
-rw-r--r-- | src/junit/runner/TestCaseClassLoader.java | 225 | ||||
-rw-r--r-- | src/junit/runner/TestCollector.java | 17 | ||||
-rw-r--r-- | src/junit/runner/excluded.properties | 12 | ||||
-rw-r--r-- | src/junit/runner/package.html | 5 |
11 files changed, 523 insertions, 0 deletions
@@ -27,6 +27,14 @@ src/junit/framework/TestSuite.java # junit-runner-files := \ src/junit/runner/BaseTestRunner.java \ +src/junit/runner/ClassPathTestCollector.java \ +src/junit/runner/FailureDetailView.java \ +src/junit/runner/LoadingTestCollector.java \ +src/junit/runner/ReloadingTestSuiteLoader.java \ +src/junit/runner/SimpleTestCollector.java \ +src/junit/runner/Sorter.java \ +src/junit/runner/TestCaseClassLoader.java \ +src/junit/runner/TestCollector.java \ src/junit/runner/TestRunListener.java \ src/junit/runner/TestSuiteLoader.java \ src/junit/runner/StandardTestSuiteLoader.java \ diff --git a/src/junit/runner/ClassPathTestCollector.java b/src/junit/runner/ClassPathTestCollector.java new file mode 100644 index 0000000..f48ddee --- /dev/null +++ b/src/junit/runner/ClassPathTestCollector.java @@ -0,0 +1,81 @@ +package junit.runner; + +import java.util.*; +import java.io.*; + +/** + * An implementation of a TestCollector that consults the + * class path. It considers all classes on the class path + * excluding classes in JARs. It leaves it up to subclasses + * to decide whether a class is a runnable Test. + * + * @see TestCollector + * {@hide} - Not needed for 1.0 SDK + */ +public abstract class ClassPathTestCollector implements TestCollector { + + static final int SUFFIX_LENGTH= ".class".length(); + + public ClassPathTestCollector() { + } + + public Enumeration collectTests() { + String classPath= System.getProperty("java.class.path"); + Hashtable result = collectFilesInPath(classPath); + return result.elements(); + } + + public Hashtable collectFilesInPath(String classPath) { + Hashtable result= collectFilesInRoots(splitClassPath(classPath)); + return result; + } + + Hashtable collectFilesInRoots(Vector roots) { + Hashtable result= new Hashtable(100); + Enumeration e= roots.elements(); + while (e.hasMoreElements()) + gatherFiles(new File((String)e.nextElement()), "", result); + return result; + } + + void gatherFiles(File classRoot, String classFileName, Hashtable result) { + File thisRoot= new File(classRoot, classFileName); + if (thisRoot.isFile()) { + if (isTestClass(classFileName)) { + String className= classNameFromFile(classFileName); + result.put(className, className); + } + return; + } + String[] contents= thisRoot.list(); + if (contents != null) { + for (int i= 0; i < contents.length; i++) + gatherFiles(classRoot, classFileName+File.separatorChar+contents[i], result); + } + } + + Vector splitClassPath(String classPath) { + Vector result= new Vector(); + String separator= System.getProperty("path.separator"); + StringTokenizer tokenizer= new StringTokenizer(classPath, separator); + while (tokenizer.hasMoreTokens()) + result.addElement(tokenizer.nextToken()); + return result; + } + + protected boolean isTestClass(String classFileName) { + return + classFileName.endsWith(".class") && + classFileName.indexOf('$') < 0 && + classFileName.indexOf("Test") > 0; + } + + 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(".")) + return s2.substring(1); + return s2; + } +} diff --git a/src/junit/runner/FailureDetailView.java b/src/junit/runner/FailureDetailView.java new file mode 100644 index 0000000..c846191 --- /dev/null +++ b/src/junit/runner/FailureDetailView.java @@ -0,0 +1,28 @@ +package junit.runner; + +// The following line was removed for compatibility with Android libraries. +//import java.awt.Component; + +import junit.framework.*; + +/** + * A view to show a details about a failure + * {@hide} - Not needed for 1.0 SDK + */ +public interface FailureDetailView { + // The following definition was removed for compatibility with Android + // libraries. + // /** + // * Returns the component used to present the TraceView + // */ + // public Component getComponent(); + + /** + * Shows details of a TestFailure + */ + public void showFailure(TestFailure failure); + /** + * Clears the view + */ + public void clear(); +} diff --git a/src/junit/runner/LoadingTestCollector.java b/src/junit/runner/LoadingTestCollector.java new file mode 100644 index 0000000..9101900 --- /dev/null +++ b/src/junit/runner/LoadingTestCollector.java @@ -0,0 +1,69 @@ +package junit.runner; + +import java.lang.reflect.*; +import junit.framework.*; + +/** + * An implementation of a TestCollector that loads + * all classes on the class path and tests whether + * it is assignable from Test or provides a static suite method. + * @see TestCollector + * {@hide} - Not needed for 1.0 SDK + */ +public class LoadingTestCollector extends ClassPathTestCollector { + + TestCaseClassLoader fLoader; + + public LoadingTestCollector() { + fLoader= new TestCaseClassLoader(); + } + + protected boolean isTestClass(String classFileName) { + try { + if (classFileName.endsWith(".class")) { + Class testClass= classFromFile(classFileName); + return (testClass != null) && isTestClass(testClass); + } + } + catch (ClassNotFoundException expected) { + } + catch (NoClassDefFoundError notFatal) { + } + return false; + } + + Class classFromFile(String classFileName) throws ClassNotFoundException { + String className= classNameFromFile(classFileName); + if (!fLoader.isExcluded(className)) + return fLoader.loadClass(className, false); + return null; + } + + boolean isTestClass(Class testClass) { + if (hasSuiteMethod(testClass)) + return true; + if (Test.class.isAssignableFrom(testClass) && + Modifier.isPublic(testClass.getModifiers()) && + hasPublicConstructor(testClass)) + return true; + return false; + } + + boolean hasSuiteMethod(Class testClass) { + try { + testClass.getMethod(BaseTestRunner.SUITE_METHODNAME, new Class[0]); + } catch(Exception e) { + return false; + } + return true; + } + + boolean hasPublicConstructor(Class testClass) { + try { + TestSuite.getTestConstructor(testClass); + } catch(NoSuchMethodException e) { + return false; + } + return true; + } +} diff --git a/src/junit/runner/ReloadingTestSuiteLoader.java b/src/junit/runner/ReloadingTestSuiteLoader.java new file mode 100644 index 0000000..c4d80d0 --- /dev/null +++ b/src/junit/runner/ReloadingTestSuiteLoader.java @@ -0,0 +1,20 @@ +package junit.runner; + +/** + * A TestSuite loader that can reload classes. + * {@hide} - Not needed for 1.0 SDK + */ +public class ReloadingTestSuiteLoader implements TestSuiteLoader { + + public Class load(String suiteClassName) throws ClassNotFoundException { + return createLoader().loadClass(suiteClassName, true); + } + + public Class reload(Class aClass) throws ClassNotFoundException { + return createLoader().loadClass(aClass.getName(), true); + } + + protected TestCaseClassLoader createLoader() { + return new TestCaseClassLoader(); + } +} diff --git a/src/junit/runner/SimpleTestCollector.java b/src/junit/runner/SimpleTestCollector.java new file mode 100644 index 0000000..6cb0e19 --- /dev/null +++ b/src/junit/runner/SimpleTestCollector.java @@ -0,0 +1,21 @@ +package junit.runner; + +/** + * An implementation of a TestCollector that considers + * a class to be a test class when it contains the + * pattern "Test" in its name + * @see TestCollector + * {@hide} - Not needed for 1.0 SDK + */ +public class SimpleTestCollector extends ClassPathTestCollector { + + public SimpleTestCollector() { + } + + protected boolean isTestClass(String classFileName) { + return + classFileName.endsWith(".class") && + classFileName.indexOf('$') < 0 && + classFileName.indexOf("Test") > 0; + } +} diff --git a/src/junit/runner/Sorter.java b/src/junit/runner/Sorter.java new file mode 100644 index 0000000..8d9341d --- /dev/null +++ b/src/junit/runner/Sorter.java @@ -0,0 +1,37 @@ +package junit.runner; + +import java.util.*; + +/** + * A custom quick sort with support to customize the swap behaviour. + * NOTICE: We can't use the the sorting support from the JDK 1.2 collection + * classes because of the JDK 1.1.7 compatibility. + * {@hide} - Not needed for 1.0 SDK + */ +public class Sorter { + public static interface Swapper { + public void swap(Vector values, int left, int right); + } + + public static void sortStrings(Vector values , int left, int right, Swapper swapper) { + int oleft= left; + int oright= right; + String mid= (String)values.elementAt((left + right) / 2); + do { + while (((String)(values.elementAt(left))).compareTo(mid) < 0) + left++; + while (mid.compareTo((String)(values.elementAt(right))) < 0) + right--; + if (left <= right) { + swapper.swap(values, left, right); + left++; + right--; + } + } while (left <= right); + + if (oleft < right) + sortStrings(values, oleft, right, swapper); + if (left < oright) + sortStrings(values, left, oright, swapper); + } +} diff --git a/src/junit/runner/TestCaseClassLoader.java b/src/junit/runner/TestCaseClassLoader.java new file mode 100644 index 0000000..09eec7f --- /dev/null +++ b/src/junit/runner/TestCaseClassLoader.java @@ -0,0 +1,225 @@ +package junit.runner; + +import java.util.*; +import java.io.*; +import java.net.URL; +import java.util.zip.*; + +/** + * A custom class loader which enables the reloading + * of classes for each test run. The class loader + * can be configured with a list of package paths that + * should be excluded from loading. The loading + * of these packages is delegated to the system class + * loader. They will be shared across test runs. + * <p> + * The list of excluded package paths is specified in + * a properties file "excluded.properties" that is located in + * the same place as the TestCaseClassLoader class. + * <p> + * <b>Known limitation:</b> the TestCaseClassLoader cannot load classes + * from jar files. + * {@hide} - Not needed for 1.0 SDK + */ +public class TestCaseClassLoader extends ClassLoader { + /** scanned class path */ + private Vector fPathItems; + /** default excluded paths */ + private String[] defaultExclusions= { + "junit.framework.", + "junit.extensions.", + "junit.runner." + }; + /** name of excluded properties file */ + static final String EXCLUDED_FILE= "excluded.properties"; + /** excluded paths */ + private Vector fExcluded; + + /** + * Constructs a TestCaseLoader. It scans the class path + * and the excluded package paths + */ + public TestCaseClassLoader() { + this(System.getProperty("java.class.path")); + } + + /** + * Constructs a TestCaseLoader. It scans the class path + * and the excluded package paths + */ + public TestCaseClassLoader(String classPath) { + scanPath(classPath); + readExcludedPackages(); + } + + private void scanPath(String classPath) { + String separator= System.getProperty("path.separator"); + fPathItems= new Vector(10); + StringTokenizer st= new StringTokenizer(classPath, separator); + while (st.hasMoreTokens()) { + fPathItems.addElement(st.nextToken()); + } + } + + public URL getResource(String name) { + return ClassLoader.getSystemResource(name); + } + + public InputStream getResourceAsStream(String name) { + return ClassLoader.getSystemResourceAsStream(name); + } + + public boolean isExcluded(String name) { + for (int i= 0; i < fExcluded.size(); i++) { + if (name.startsWith((String) fExcluded.elementAt(i))) { + return true; + } + } + return false; + } + + public synchronized Class loadClass(String name, boolean resolve) + throws ClassNotFoundException { + + Class c= findLoadedClass(name); + if (c != null) + return c; + // + // Delegate the loading of excluded classes to the + // standard class loader. + // + if (isExcluded(name)) { + try { + c= findSystemClass(name); + return c; + } catch (ClassNotFoundException e) { + // keep searching + } + } + if (c == null) { + byte[] data= lookupClassData(name); + if (data == null) + throw new ClassNotFoundException(); + c= defineClass(name, data, 0, data.length); + } + if (resolve) + resolveClass(c); + return c; + } + + private byte[] lookupClassData(String className) throws ClassNotFoundException { + byte[] data= null; + for (int i= 0; i < fPathItems.size(); i++) { + String path= (String) fPathItems.elementAt(i); + String fileName= className.replace('.', '/')+".class"; + if (isJar(path)) { + data= loadJarData(path, fileName); + } else { + data= loadFileData(path, fileName); + } + if (data != null) + return data; + } + throw new ClassNotFoundException(className); + } + + boolean isJar(String pathEntry) { + return pathEntry.endsWith(".jar") || + pathEntry.endsWith(".apk") || + pathEntry.endsWith(".zip"); + } + + private byte[] loadFileData(String path, String fileName) { + File file= new File(path, fileName); + if (file.exists()) { + return getClassData(file); + } + return null; + } + + private byte[] getClassData(File f) { + try { + FileInputStream stream= new FileInputStream(f); + ByteArrayOutputStream out= new ByteArrayOutputStream(1000); + byte[] b= new byte[1000]; + int n; + while ((n= stream.read(b)) != -1) + out.write(b, 0, n); + stream.close(); + out.close(); + return out.toByteArray(); + + } catch (IOException e) { + } + return null; + } + + private byte[] loadJarData(String path, String fileName) { + ZipFile zipFile= null; + InputStream stream= null; + File archive= new File(path); + if (!archive.exists()) + return null; + try { + zipFile= new ZipFile(archive); + } catch(IOException io) { + return null; + } + ZipEntry entry= zipFile.getEntry(fileName); + if (entry == null) + return null; + int size= (int) entry.getSize(); + try { + stream= zipFile.getInputStream(entry); + byte[] data= new byte[size]; + int pos= 0; + while (pos < size) { + int n= stream.read(data, pos, data.length - pos); + pos += n; + } + zipFile.close(); + return data; + } catch (IOException e) { + } finally { + try { + if (stream != null) + stream.close(); + } catch (IOException e) { + } + } + return null; + } + + private void readExcludedPackages() { + fExcluded= new Vector(10); + for (int i= 0; i < defaultExclusions.length; i++) + fExcluded.addElement(defaultExclusions[i]); + + InputStream is= getClass().getResourceAsStream(EXCLUDED_FILE); + if (is == null) + return; + Properties p= new Properties(); + try { + p.load(is); + } + catch (IOException e) { + return; + } finally { + try { + is.close(); + } catch (IOException e) { + } + } + for (Enumeration e= p.propertyNames(); e.hasMoreElements(); ) { + String key= (String)e.nextElement(); + if (key.startsWith("excluded.")) { + String path= p.getProperty(key); + path= path.trim(); + if (path.endsWith("*")) + path= path.substring(0, path.length()-1); + if (path.length() > 0) + fExcluded.addElement(path); + } + } + } +} diff --git a/src/junit/runner/TestCollector.java b/src/junit/runner/TestCollector.java new file mode 100644 index 0000000..3ac9d9e --- /dev/null +++ b/src/junit/runner/TestCollector.java @@ -0,0 +1,17 @@ +package junit.runner; + +import java.util.*; + + +/** + * Collects Test class names to be presented + * by the TestSelector. + * @see TestSelector + * {@hide} - Not needed for 1.0 SDK + */ +public interface TestCollector { + /** + * Returns an enumeration of Strings with qualified class names + */ + public Enumeration collectTests(); +} diff --git a/src/junit/runner/excluded.properties b/src/junit/runner/excluded.properties new file mode 100644 index 0000000..3284628 --- /dev/null +++ b/src/junit/runner/excluded.properties @@ -0,0 +1,12 @@ +# +# The list of excluded package paths for the TestCaseClassLoader +# +excluded.0=sun.* +excluded.1=com.sun.* +excluded.2=org.omg.* +excluded.3=javax.* +excluded.4=sunw.* +excluded.5=java.* +excluded.6=org.w3c.dom.* +excluded.7=org.xml.sax.* +excluded.8=net.jini.* diff --git a/src/junit/runner/package.html b/src/junit/runner/package.html new file mode 100644 index 0000000..f08fa70 --- /dev/null +++ b/src/junit/runner/package.html @@ -0,0 +1,5 @@ +<HTML> +<BODY> +Utility classes supporting the junit test framework. +</BODY> +</HTML> |