aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Common.mk8
-rw-r--r--src/junit/runner/ClassPathTestCollector.java81
-rw-r--r--src/junit/runner/FailureDetailView.java28
-rw-r--r--src/junit/runner/LoadingTestCollector.java69
-rw-r--r--src/junit/runner/ReloadingTestSuiteLoader.java20
-rw-r--r--src/junit/runner/SimpleTestCollector.java21
-rw-r--r--src/junit/runner/Sorter.java37
-rw-r--r--src/junit/runner/TestCaseClassLoader.java225
-rw-r--r--src/junit/runner/TestCollector.java17
-rw-r--r--src/junit/runner/excluded.properties12
-rw-r--r--src/junit/runner/package.html5
11 files changed, 523 insertions, 0 deletions
diff --git a/Common.mk b/Common.mk
index ed6b923..0c86b4f 100644
--- a/Common.mk
+++ b/Common.mk
@@ -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>