diff options
author | Brett Chabot <brettchabot@android.com> | 2010-02-19 10:02:10 -0800 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2010-02-19 10:02:10 -0800 |
commit | 9db619e5d3420c53d94866a02b8f3ed298afe636 (patch) | |
tree | b968c141cff010a5230659643aea5df21a1ef0f4 /test-runner/src/junit | |
parent | 884ed0190c46faf3fc0e156a80a7bdb133712ed1 (diff) | |
parent | 12093976a4842a795491cfd2b1d3b71e18503f2d (diff) | |
download | frameworks_base-9db619e5d3420c53d94866a02b8f3ed298afe636.zip frameworks_base-9db619e5d3420c53d94866a02b8f3ed298afe636.tar.gz frameworks_base-9db619e5d3420c53d94866a02b8f3ed298afe636.tar.bz2 |
Merge "Move framework test-runner unit tests to be closer to their source. Move the test-runner source into a separate src folder to accommodate the test move."
Diffstat (limited to 'test-runner/src/junit')
21 files changed, 1245 insertions, 0 deletions
diff --git a/test-runner/src/junit/MODULE_LICENSE_CPL b/test-runner/src/junit/MODULE_LICENSE_CPL new file mode 100644 index 0000000..541dbb5 --- /dev/null +++ b/test-runner/src/junit/MODULE_LICENSE_CPL @@ -0,0 +1 @@ +http://www.opensource.org/licenses/cpl1.0.php diff --git a/test-runner/src/junit/runner/BaseTestRunner.java b/test-runner/src/junit/runner/BaseTestRunner.java new file mode 100644 index 0000000..e073ef7 --- /dev/null +++ b/test-runner/src/junit/runner/BaseTestRunner.java @@ -0,0 +1,326 @@ +package junit.runner; + +import junit.framework.*; +import java.lang.reflect.*; +import java.text.NumberFormat; +import java.io.*; +import java.util.*; + +/** + * Base class for all test runners. + * This class was born live on stage in Sardinia during XP2000. + */ +public abstract class BaseTestRunner implements TestListener { + public static final String SUITE_METHODNAME= "suite"; + + private static Properties fPreferences; + static int fgMaxMessageLength= 500; + static boolean fgFilterStack= true; + boolean fLoading= true; + + /* + * Implementation of TestListener + */ + public synchronized void startTest(Test test) { + testStarted(test.toString()); + } + + protected static void setPreferences(Properties preferences) { + fPreferences= preferences; + } + + protected static Properties getPreferences() { + if (fPreferences == null) { + fPreferences= new Properties(); + fPreferences.put("loading", "true"); + fPreferences.put("filterstack", "true"); + readPreferences(); + } + return fPreferences; + } + + public static void savePreferences() throws IOException { + FileOutputStream fos= new FileOutputStream(getPreferencesFile()); + try { + getPreferences().store(fos, ""); + } finally { + fos.close(); + } + } + + public void setPreference(String key, String value) { + getPreferences().setProperty(key, value); + } + + public synchronized void endTest(Test test) { + testEnded(test.toString()); + } + + public synchronized void addError(final Test test, final Throwable t) { + testFailed(TestRunListener.STATUS_ERROR, test, t); + } + + public synchronized void addFailure(final Test test, final AssertionFailedError t) { + testFailed(TestRunListener.STATUS_FAILURE, test, t); + } + + // TestRunListener implementation + + public abstract void testStarted(String testName); + + public abstract void testEnded(String testName); + + public abstract void testFailed(int status, Test test, Throwable t); + + /** + * Returns the Test corresponding to the given suite. This is + * a template method, subclasses override runFailed(), clearStatus(). + */ + public Test getTest(String suiteClassName) { + if (suiteClassName.length() <= 0) { + clearStatus(); + return null; + } + Class testClass= null; + try { + testClass= loadSuiteClass(suiteClassName); + } catch (ClassNotFoundException e) { + String clazz= e.getMessage(); + if (clazz == null) + clazz= suiteClassName; + runFailed("Class not found \""+clazz+"\""); + return null; + } catch(Exception e) { + runFailed("Error: "+e.toString()); + return null; + } + Method suiteMethod= null; + try { + suiteMethod= testClass.getMethod(SUITE_METHODNAME, new Class[0]); + } catch(Exception e) { + // try to extract a test suite automatically + clearStatus(); + return new TestSuite(testClass); + } + if (! Modifier.isStatic(suiteMethod.getModifiers())) { + runFailed("Suite() method must be static"); + return null; + } + Test test= null; + try { + test= (Test)suiteMethod.invoke(null); // static method + if (test == null) + return test; + } + catch (InvocationTargetException e) { + runFailed("Failed to invoke suite():" + e.getTargetException().toString()); + return null; + } + catch (IllegalAccessException e) { + runFailed("Failed to invoke suite():" + e.toString()); + return null; + } + + clearStatus(); + return test; + } + + /** + * Returns the formatted string of the elapsed time. + */ + public String elapsedTimeAsString(long runTime) { + return NumberFormat.getInstance().format((double)runTime/1000); + } + + /** + * Processes the command line arguments and + * returns the name of the suite class to run or null + */ + protected String processArguments(String[] args) { + String suiteName= null; + for (int i= 0; i < args.length; i++) { + if (args[i].equals("-noloading")) { + setLoading(false); + } else if (args[i].equals("-nofilterstack")) { + fgFilterStack= false; + } else if (args[i].equals("-c")) { + if (args.length > i+1) + suiteName= extractClassName(args[i+1]); + else + System.out.println("Missing Test class name"); + i++; + } else { + suiteName= args[i]; + } + } + return suiteName; + } + + /** + * Sets the loading behaviour of the test runner + */ + public void setLoading(boolean enable) { + fLoading= enable; + } + /** + * Extract the class name from a String + */ + public String extractClassName(String className) { + if(className.startsWith("Default package for")) + return className.substring(className.lastIndexOf(".")+1); + return className; + } + + /** + * Truncates a String to the maximum length. + */ + public static String truncate(String s) { + if (fgMaxMessageLength != -1 && s.length() > fgMaxMessageLength) + s= s.substring(0, fgMaxMessageLength)+"..."; + return s; + } + + /** + * Override to define how to handle a failed loading of + * a test suite. + */ + protected abstract void runFailed(String message); + + /** + * Returns the loaded Class for a suite name. + */ + protected Class loadSuiteClass(String suiteClassName) throws ClassNotFoundException { + return getLoader().load(suiteClassName); + } + + /** + * Clears the status message. + */ + protected void clearStatus() { // Belongs in the GUI TestRunner class + } + + /** + * Returns the loader to be used. + */ + public TestSuiteLoader getLoader() { + if (useReloadingTestSuiteLoader()) + return new ReloadingTestSuiteLoader(); + return new StandardTestSuiteLoader(); + } + + protected boolean useReloadingTestSuiteLoader() { + return getPreference("loading").equals("true") && !inVAJava() && fLoading; + } + + private static File getPreferencesFile() { + String home= System.getProperty("user.home"); + return new File(home, "junit.properties"); + } + + private static void readPreferences() { + InputStream is= null; + try { + is= new FileInputStream(getPreferencesFile()); + setPreferences(new Properties(getPreferences())); + getPreferences().load(is); + } catch (IOException e) { + try { + if (is != null) + is.close(); + } catch (IOException e1) { + } + } + } + + public static String getPreference(String key) { + return getPreferences().getProperty(key); + } + + public static int getPreference(String key, int dflt) { + String value= getPreference(key); + int intValue= dflt; + if (value == null) + return intValue; + try { + intValue= Integer.parseInt(value); + } catch (NumberFormatException ne) { + } + return intValue; + } + + public static boolean inVAJava() { + try { + Class.forName("com.ibm.uvm.tools.DebugSupport"); + } + catch (Exception e) { + return false; + } + return true; + } + + /** + * Returns a filtered stack trace + */ + public static String getFilteredTrace(Throwable t) { + StringWriter stringWriter= new StringWriter(); + PrintWriter writer= new PrintWriter(stringWriter); + t.printStackTrace(writer); + StringBuffer buffer= stringWriter.getBuffer(); + String trace= buffer.toString(); + return BaseTestRunner.getFilteredTrace(trace); + } + + /** + * Filters stack frames from internal JUnit classes + */ + public static String getFilteredTrace(String stack) { + if (showStackRaw()) + return stack; + + StringWriter sw= new StringWriter(); + PrintWriter pw= new PrintWriter(sw); + StringReader sr= new StringReader(stack); + // BEGIN android-changed + // Use a sensible default buffer size + BufferedReader br= new BufferedReader(sr, 1000); + // END android-changed + + String line; + try { + while ((line= br.readLine()) != null) { + if (!filterLine(line)) + pw.println(line); + } + } catch (Exception IOException) { + return stack; // return the stack unfiltered + } + return sw.toString(); + } + + protected static boolean showStackRaw() { + return !getPreference("filterstack").equals("true") || fgFilterStack == false; + } + + static boolean filterLine(String line) { + String[] patterns= new String[] { + "junit.framework.TestCase", + "junit.framework.TestResult", + "junit.framework.TestSuite", + "junit.framework.Assert.", // don't filter AssertionFailure + "junit.swingui.TestRunner", + "junit.awtui.TestRunner", + "junit.textui.TestRunner", + "java.lang.reflect.Method.invoke(" + }; + for (int i= 0; i < patterns.length; i++) { + if (line.indexOf(patterns[i]) > 0) + return true; + } + return false; + } + + static { + fgMaxMessageLength= getPreference("maxmessage", fgMaxMessageLength); + } + +} diff --git a/test-runner/src/junit/runner/ClassPathTestCollector.java b/test-runner/src/junit/runner/ClassPathTestCollector.java new file mode 100644 index 0000000..8a3c702 --- /dev/null +++ b/test-runner/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/test-runner/src/junit/runner/FailureDetailView.java b/test-runner/src/junit/runner/FailureDetailView.java new file mode 100644 index 0000000..7108cec --- /dev/null +++ b/test-runner/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/test-runner/src/junit/runner/LoadingTestCollector.java b/test-runner/src/junit/runner/LoadingTestCollector.java new file mode 100644 index 0000000..b1760b1 --- /dev/null +++ b/test-runner/src/junit/runner/LoadingTestCollector.java @@ -0,0 +1,70 @@ +package junit.runner; + +import java.lang.reflect.*; +import junit.runner.*; +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/test-runner/src/junit/runner/ReloadingTestSuiteLoader.java b/test-runner/src/junit/runner/ReloadingTestSuiteLoader.java new file mode 100644 index 0000000..a6d84fe --- /dev/null +++ b/test-runner/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/test-runner/src/junit/runner/SimpleTestCollector.java b/test-runner/src/junit/runner/SimpleTestCollector.java new file mode 100644 index 0000000..543168f --- /dev/null +++ b/test-runner/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/test-runner/src/junit/runner/Sorter.java b/test-runner/src/junit/runner/Sorter.java new file mode 100644 index 0000000..66f551e --- /dev/null +++ b/test-runner/src/junit/runner/Sorter.java @@ -0,0 +1,39 @@ +package junit.runner; + +import java.util.*; + +import junit.runner.*; + +/** + * 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/test-runner/src/junit/runner/StandardTestSuiteLoader.java b/test-runner/src/junit/runner/StandardTestSuiteLoader.java new file mode 100644 index 0000000..bce7dec --- /dev/null +++ b/test-runner/src/junit/runner/StandardTestSuiteLoader.java @@ -0,0 +1,20 @@ +package junit.runner; + +/** + * The standard test suite loader. It can only load the same class once. + * {@hide} - Not needed for 1.0 SDK + */ +public class StandardTestSuiteLoader implements TestSuiteLoader { + /** + * Uses the system class loader to load the test class + */ + public Class load(String suiteClassName) throws ClassNotFoundException { + return Class.forName(suiteClassName); + } + /** + * Uses the system class loader to load the test class + */ + public Class reload(Class aClass) throws ClassNotFoundException { + return aClass; + } +} diff --git a/test-runner/src/junit/runner/TestCaseClassLoader.java b/test-runner/src/junit/runner/TestCaseClassLoader.java new file mode 100644 index 0000000..3a510c6 --- /dev/null +++ b/test-runner/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/test-runner/src/junit/runner/TestCollector.java b/test-runner/src/junit/runner/TestCollector.java new file mode 100644 index 0000000..208dccd --- /dev/null +++ b/test-runner/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/test-runner/src/junit/runner/TestRunListener.java b/test-runner/src/junit/runner/TestRunListener.java new file mode 100644 index 0000000..0e95819 --- /dev/null +++ b/test-runner/src/junit/runner/TestRunListener.java @@ -0,0 +1,20 @@ +package junit.runner; +/** + * A listener interface for observing the + * execution of a test run. Unlike TestListener, + * this interface using only primitive objects, + * making it suitable for remote test execution. + * {@hide} - Not needed for 1.0 SDK + */ + public interface TestRunListener { + /* test status constants*/ + public static final int STATUS_ERROR= 1; + public static final int STATUS_FAILURE= 2; + + public void testRunStarted(String testSuiteName, int testCount); + public void testRunEnded(long elapsedTime); + public void testRunStopped(long elapsedTime); + public void testStarted(String testName); + public void testEnded(String testName); + public void testFailed(int status, String testName, String trace); +} diff --git a/test-runner/src/junit/runner/TestSuiteLoader.java b/test-runner/src/junit/runner/TestSuiteLoader.java new file mode 100644 index 0000000..39a4cf7 --- /dev/null +++ b/test-runner/src/junit/runner/TestSuiteLoader.java @@ -0,0 +1,9 @@ +package junit.runner; + +/** + * An interface to define how a test suite should be loaded. + */ +public interface TestSuiteLoader { + abstract public Class load(String suiteClassName) throws ClassNotFoundException; + abstract public Class reload(Class aClass) throws ClassNotFoundException; +} diff --git a/test-runner/src/junit/runner/Version.java b/test-runner/src/junit/runner/Version.java new file mode 100644 index 0000000..b4541ab --- /dev/null +++ b/test-runner/src/junit/runner/Version.java @@ -0,0 +1,14 @@ +package junit.runner; + +/** + * This class defines the current version of JUnit + */ +public class Version { + private Version() { + // don't instantiate + } + + public static String id() { + return "3.8.1"; + } +} diff --git a/test-runner/src/junit/runner/excluded.properties b/test-runner/src/junit/runner/excluded.properties new file mode 100644 index 0000000..3284628 --- /dev/null +++ b/test-runner/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/test-runner/src/junit/runner/logo.gif b/test-runner/src/junit/runner/logo.gif Binary files differnew file mode 100644 index 0000000..d0e1547 --- /dev/null +++ b/test-runner/src/junit/runner/logo.gif diff --git a/test-runner/src/junit/runner/package.html b/test-runner/src/junit/runner/package.html new file mode 100644 index 0000000..f08fa70 --- /dev/null +++ b/test-runner/src/junit/runner/package.html @@ -0,0 +1,5 @@ +<HTML> +<BODY> +Utility classes supporting the junit test framework. +</BODY> +</HTML> diff --git a/test-runner/src/junit/runner/smalllogo.gif b/test-runner/src/junit/runner/smalllogo.gif Binary files differnew file mode 100644 index 0000000..7b25eaf6 --- /dev/null +++ b/test-runner/src/junit/runner/smalllogo.gif diff --git a/test-runner/src/junit/textui/ResultPrinter.java b/test-runner/src/junit/textui/ResultPrinter.java new file mode 100644 index 0000000..5c97112 --- /dev/null +++ b/test-runner/src/junit/textui/ResultPrinter.java @@ -0,0 +1,142 @@ + +package junit.textui; + +import java.io.PrintStream; +// The following line was removed for compatibility with Android libraries. +//import java.text.NumberFormat; +import java.util.Enumeration; + +import junit.framework.AssertionFailedError; +import junit.framework.Test; +import junit.framework.TestFailure; +import junit.framework.TestListener; +import junit.framework.TestResult; +import junit.runner.BaseTestRunner; + +public class ResultPrinter implements TestListener { + PrintStream fWriter; + int fColumn= 0; + + public ResultPrinter(PrintStream writer) { + fWriter= writer; + } + + /* API for use by textui.TestRunner + */ + + synchronized void print(TestResult result, long runTime) { + printHeader(runTime); + printErrors(result); + printFailures(result); + printFooter(result); + } + + void printWaitPrompt() { + getWriter().println(); + getWriter().println("<RETURN> to continue"); + } + + /* Internal methods + */ + + protected void printHeader(long runTime) { + getWriter().println(); + getWriter().println("Time: "+elapsedTimeAsString(runTime)); + } + + protected void printErrors(TestResult result) { + printDefects(result.errors(), result.errorCount(), "error"); + } + + protected void printFailures(TestResult result) { + printDefects(result.failures(), result.failureCount(), "failure"); + } + + protected void printDefects(Enumeration booBoos, int count, String type) { + if (count == 0) return; + if (count == 1) + getWriter().println("There was " + count + " " + type + ":"); + else + getWriter().println("There were " + count + " " + type + "s:"); + for (int i= 1; booBoos.hasMoreElements(); i++) { + printDefect((TestFailure) booBoos.nextElement(), i); + } + } + + public void printDefect(TestFailure booBoo, int count) { // only public for testing purposes + printDefectHeader(booBoo, count); + printDefectTrace(booBoo); + } + + protected void printDefectHeader(TestFailure booBoo, int count) { + // I feel like making this a println, then adding a line giving the throwable a chance to print something + // before we get to the stack trace. + getWriter().print(count + ") " + booBoo.failedTest()); + } + + protected void printDefectTrace(TestFailure booBoo) { + getWriter().print(BaseTestRunner.getFilteredTrace(booBoo.trace())); + } + + protected void printFooter(TestResult result) { + if (result.wasSuccessful()) { + getWriter().println(); + getWriter().print("OK"); + getWriter().println (" (" + result.runCount() + " test" + (result.runCount() == 1 ? "": "s") + ")"); + + } else { + getWriter().println(); + getWriter().println("FAILURES!!!"); + getWriter().println("Tests run: "+result.runCount()+ + ", Failures: "+result.failureCount()+ + ", Errors: "+result.errorCount()); + } + getWriter().println(); + } + + + /** + * Returns the formatted string of the elapsed time. + * Duplicated from BaseTestRunner. Fix it. + */ + protected String elapsedTimeAsString(long runTime) { + // The following line was altered for compatibility with + // Android libraries. + return Double.toString((double)runTime/1000); + } + + public PrintStream getWriter() { + return fWriter; + } + /** + * @see junit.framework.TestListener#addError(Test, Throwable) + */ + public void addError(Test test, Throwable t) { + getWriter().print("E"); + } + + /** + * @see junit.framework.TestListener#addFailure(Test, AssertionFailedError) + */ + public void addFailure(Test test, AssertionFailedError t) { + getWriter().print("F"); + } + + /** + * @see junit.framework.TestListener#endTest(Test) + */ + public void endTest(Test test) { + } + + /** + * @see junit.framework.TestListener#startTest(Test) + */ + public void startTest(Test test) { + getWriter().print("."); + if (fColumn++ >= 40) { + getWriter().println(); + fColumn= 0; + } + } + +} diff --git a/test-runner/src/junit/textui/TestRunner.java b/test-runner/src/junit/textui/TestRunner.java new file mode 100644 index 0000000..8bdc325 --- /dev/null +++ b/test-runner/src/junit/textui/TestRunner.java @@ -0,0 +1,189 @@ +package junit.textui; + + +import java.io.PrintStream; + +import junit.framework.*; +import junit.runner.*; + +/** + * A command line based tool to run tests. + * <pre> + * java junit.textui.TestRunner [-wait] TestCaseClass + * </pre> + * TestRunner expects the name of a TestCase class as argument. + * If this class defines a static <code>suite</code> method it + * will be invoked and the returned test is run. Otherwise all + * the methods starting with "test" having no arguments are run. + * <p> + * When the wait command line argument is given TestRunner + * waits until the users types RETURN. + * <p> + * TestRunner prints a trace as the tests are executed followed by a + * summary at the end. + */ +public class TestRunner extends BaseTestRunner { + private ResultPrinter fPrinter; + + public static final int SUCCESS_EXIT= 0; + public static final int FAILURE_EXIT= 1; + public static final int EXCEPTION_EXIT= 2; + + /** + * Constructs a TestRunner. + */ + public TestRunner() { + this(System.out); + } + + /** + * Constructs a TestRunner using the given stream for all the output + */ + public TestRunner(PrintStream writer) { + this(new ResultPrinter(writer)); + } + + /** + * Constructs a TestRunner using the given ResultPrinter all the output + */ + public TestRunner(ResultPrinter printer) { + fPrinter= printer; + } + + /** + * Runs a suite extracted from a TestCase subclass. + */ + static public void run(Class testClass) { + run(new TestSuite(testClass)); + } + + /** + * Runs a single test and collects its results. + * This method can be used to start a test run + * from your program. + * <pre> + * public static void main (String[] args) { + * test.textui.TestRunner.run(suite()); + * } + * </pre> + */ + static public TestResult run(Test test) { + TestRunner runner= new TestRunner(); + return runner.doRun(test); + } + + /** + * Runs a single test and waits until the user + * types RETURN. + */ + static public void runAndWait(Test suite) { + TestRunner aTestRunner= new TestRunner(); + aTestRunner.doRun(suite, true); + } + + /** + * Always use the StandardTestSuiteLoader. Overridden from + * BaseTestRunner. + */ + public TestSuiteLoader getLoader() { + return new StandardTestSuiteLoader(); + } + + public void testFailed(int status, Test test, Throwable t) { + } + + public void testStarted(String testName) { + } + + public void testEnded(String testName) { + } + + /** + * Creates the TestResult to be used for the test run. + */ + protected TestResult createTestResult() { + return new TestResult(); + } + + public TestResult doRun(Test test) { + return doRun(test, false); + } + + public TestResult doRun(Test suite, boolean wait) { + TestResult result= createTestResult(); + result.addListener(fPrinter); + long startTime= System.currentTimeMillis(); + suite.run(result); + long endTime= System.currentTimeMillis(); + long runTime= endTime-startTime; + fPrinter.print(result, runTime); + + pause(wait); + return result; + } + + protected void pause(boolean wait) { + if (!wait) return; + fPrinter.printWaitPrompt(); + try { + System.in.read(); + } + catch(Exception e) { + } + } + + public static void main(String args[]) { + TestRunner aTestRunner= new TestRunner(); + try { + TestResult r= aTestRunner.start(args); + if (!r.wasSuccessful()) + System.exit(FAILURE_EXIT); + System.exit(SUCCESS_EXIT); + } catch(Exception e) { + System.err.println(e.getMessage()); + System.exit(EXCEPTION_EXIT); + } + } + + /** + * Starts a test run. Analyzes the command line arguments + * and runs the given test suite. + */ + protected TestResult start(String args[]) throws Exception { + String testCase= ""; + boolean wait= false; + + for (int i= 0; i < args.length; i++) { + if (args[i].equals("-wait")) + wait= true; + else if (args[i].equals("-c")) + testCase= extractClassName(args[++i]); + else if (args[i].equals("-v")) + System.err.println("JUnit "+Version.id()+" by Kent Beck and Erich Gamma"); + else + testCase= args[i]; + } + + if (testCase.equals("")) + throw new Exception("Usage: TestRunner [-wait] testCaseName, where name is the name of the TestCase class"); + + try { + Test suite= getTest(testCase); + return doRun(suite, wait); + } + catch(Exception e) { + throw new Exception("Could not create and run test suite: "+e); + } + } + + protected void runFailed(String message) { + System.err.println(message); + System.exit(FAILURE_EXIT); + } + + public void setPrinter(ResultPrinter printer) { + fPrinter= printer; + } + + +} diff --git a/test-runner/src/junit/textui/package.html b/test-runner/src/junit/textui/package.html new file mode 100644 index 0000000..723f2ae --- /dev/null +++ b/test-runner/src/junit/textui/package.html @@ -0,0 +1,6 @@ +<HTML> +<BODY> +Utility classes supporting the junit test framework. +{@hide} - Not needed for 1.0 SDK +</BODY> +</HTML> |