package junit.runner; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; import java.io.StringReader; import java.io.StringWriter; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.text.NumberFormat; import java.util.Properties; import junit.framework.AssertionFailedError; import junit.framework.Test; import junit.framework.TestListener; import junit.framework.TestSuite; /** * 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(); } } // android-changed remove 'static' qualifier for API compatibility public void setPreference(String key, String value) { getPreferences().put(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, (Object[])new Class[0]); // 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 in VA/Java style */ 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); // BEGIN android-changed - add back getLoader() for API compatibility /** * Returns the loader to be used. * * @deprecated not present in JUnit4.10 */ public TestSuiteLoader getLoader() { return new StandardTestSuiteLoader(); } // END android-changed /** * Returns the loaded Class for a suite name. */ protected Class loadSuiteClass(String suiteClassName) throws ClassNotFoundException { return Class.forName(suiteClassName); } /** * Clears the status message. */ protected void clearStatus() { // Belongs in the GUI TestRunner class } protected boolean useReloadingTestSuiteLoader() { return getPreference("loading").equals("true") && 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; } /** * 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); } // BEGIN android-changed - add back this method for API compatibility /** @deprecated not present in JUnit4.10 */ public static boolean inVAJava() { return false; } // END android-changed /** * 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); } }