1 package junit.runner; 2 3 import java.io.BufferedReader; 4 import java.io.File; 5 import java.io.FileInputStream; 6 import java.io.FileOutputStream; 7 import java.io.IOException; 8 import java.io.InputStream; 9 import java.io.PrintWriter; 10 import java.io.StringReader; 11 import java.io.StringWriter; 12 import java.lang.reflect.InvocationTargetException; 13 import java.lang.reflect.Method; 14 import java.lang.reflect.Modifier; 15 import java.text.NumberFormat; 16 import java.util.Properties; 17 18 import junit.framework.AssertionFailedError; 19 import junit.framework.Test; 20 import junit.framework.TestListener; 21 import junit.framework.TestSuite; 22 23 import org.junit.internal.Throwables; 24 25 /** 26 * Base class for all test runners. 27 * This class was born live on stage in Sardinia during XP2000. 28 */ 29 public abstract class BaseTestRunner implements TestListener { 30 public static final String SUITE_METHODNAME = "suite"; 31 32 private static Properties fPreferences; 33 static int fgMaxMessageLength = 500; 34 static boolean fgFilterStack = true; 35 boolean fLoading = true; 36 37 /* 38 * Implementation of TestListener 39 */ startTest(Test test)40 public synchronized void startTest(Test test) { 41 testStarted(test.toString()); 42 } 43 setPreferences(Properties preferences)44 protected static void setPreferences(Properties preferences) { 45 fPreferences = preferences; 46 } 47 getPreferences()48 protected static Properties getPreferences() { 49 if (fPreferences == null) { 50 fPreferences = new Properties(); 51 fPreferences.put("loading", "true"); 52 fPreferences.put("filterstack", "true"); 53 readPreferences(); 54 } 55 return fPreferences; 56 } 57 savePreferences()58 public static void savePreferences() throws IOException { 59 FileOutputStream fos = new FileOutputStream(getPreferencesFile()); 60 try { 61 getPreferences().store(fos, ""); 62 } finally { 63 fos.close(); 64 } 65 } 66 setPreference(String key, String value)67 public static void setPreference(String key, String value) { 68 getPreferences().put(key, value); 69 } 70 endTest(Test test)71 public synchronized void endTest(Test test) { 72 testEnded(test.toString()); 73 } 74 addError(final Test test, final Throwable e)75 public synchronized void addError(final Test test, final Throwable e) { 76 testFailed(TestRunListener.STATUS_ERROR, test, e); 77 } 78 addFailure(final Test test, final AssertionFailedError e)79 public synchronized void addFailure(final Test test, final AssertionFailedError e) { 80 testFailed(TestRunListener.STATUS_FAILURE, test, e); 81 } 82 83 // TestRunListener implementation 84 testStarted(String testName)85 public abstract void testStarted(String testName); 86 testEnded(String testName)87 public abstract void testEnded(String testName); 88 testFailed(int status, Test test, Throwable e)89 public abstract void testFailed(int status, Test test, Throwable e); 90 91 /** 92 * Returns the Test corresponding to the given suite. This is 93 * a template method, subclasses override runFailed(), clearStatus(). 94 */ getTest(String suiteClassName)95 public Test getTest(String suiteClassName) { 96 if (suiteClassName.length() <= 0) { 97 clearStatus(); 98 return null; 99 } 100 Class<?> testClass = null; 101 try { 102 testClass = loadSuiteClass(suiteClassName); 103 } catch (ClassNotFoundException e) { 104 String clazz = e.getMessage(); 105 if (clazz == null) { 106 clazz = suiteClassName; 107 } 108 runFailed("Class not found \"" + clazz + "\""); 109 return null; 110 } catch (Exception e) { 111 runFailed("Error: " + e.toString()); 112 return null; 113 } 114 Method suiteMethod = null; 115 try { 116 suiteMethod = testClass.getMethod(SUITE_METHODNAME); 117 } catch (Exception e) { 118 // try to extract a test suite automatically 119 clearStatus(); 120 return new TestSuite(testClass); 121 } 122 if (!Modifier.isStatic(suiteMethod.getModifiers())) { 123 runFailed("Suite() method must be static"); 124 return null; 125 } 126 Test test = null; 127 try { 128 test = (Test) suiteMethod.invoke(null); // static method 129 if (test == null) { 130 return test; 131 } 132 } catch (InvocationTargetException e) { 133 runFailed("Failed to invoke suite():" + e.getTargetException().toString()); 134 return null; 135 } catch (IllegalAccessException e) { 136 runFailed("Failed to invoke suite():" + e.toString()); 137 return null; 138 } 139 140 clearStatus(); 141 return test; 142 } 143 144 /** 145 * Returns the formatted string of the elapsed time. 146 */ elapsedTimeAsString(long runTime)147 public String elapsedTimeAsString(long runTime) { 148 return NumberFormat.getInstance().format((double) runTime / 1000); 149 } 150 151 /** 152 * Processes the command line arguments and 153 * returns the name of the suite class to run or null 154 */ processArguments(String[] args)155 protected String processArguments(String[] args) { 156 String suiteName = null; 157 for (int i = 0; i < args.length; i++) { 158 if (args[i].equals("-noloading")) { 159 setLoading(false); 160 } else if (args[i].equals("-nofilterstack")) { 161 fgFilterStack = false; 162 } else if (args[i].equals("-c")) { 163 if (args.length > i + 1) { 164 suiteName = extractClassName(args[i + 1]); 165 } else { 166 System.out.println("Missing Test class name"); 167 } 168 i++; 169 } else { 170 suiteName = args[i]; 171 } 172 } 173 return suiteName; 174 } 175 176 /** 177 * Sets the loading behaviour of the test runner 178 */ setLoading(boolean enable)179 public void setLoading(boolean enable) { 180 fLoading = enable; 181 } 182 183 /** 184 * Extract the class name from a String in VA/Java style 185 */ extractClassName(String className)186 public String extractClassName(String className) { 187 if (className.startsWith("Default package for")) { 188 return className.substring(className.lastIndexOf(".") + 1); 189 } 190 return className; 191 } 192 193 /** 194 * Truncates a String to the maximum length. 195 */ truncate(String s)196 public static String truncate(String s) { 197 if (fgMaxMessageLength != -1 && s.length() > fgMaxMessageLength) { 198 s = s.substring(0, fgMaxMessageLength) + "..."; 199 } 200 return s; 201 } 202 203 /** 204 * Override to define how to handle a failed loading of 205 * a test suite. 206 */ runFailed(String message)207 protected abstract void runFailed(String message); 208 209 /** 210 * Returns the loaded Class for a suite name. 211 */ loadSuiteClass(String suiteClassName)212 protected Class<?> loadSuiteClass(String suiteClassName) throws ClassNotFoundException { 213 return Class.forName(suiteClassName); 214 } 215 216 /** 217 * Clears the status message. 218 */ clearStatus()219 protected void clearStatus() { // Belongs in the GUI TestRunner class 220 } 221 useReloadingTestSuiteLoader()222 protected boolean useReloadingTestSuiteLoader() { 223 return getPreference("loading").equals("true") && fLoading; 224 } 225 getPreferencesFile()226 private static File getPreferencesFile() { 227 String home = System.getProperty("user.home"); 228 return new File(home, "junit.properties"); 229 } 230 readPreferences()231 private static void readPreferences() { 232 InputStream is = null; 233 try { 234 is = new FileInputStream(getPreferencesFile()); 235 setPreferences(new Properties(getPreferences())); 236 getPreferences().load(is); 237 } catch (IOException ignored) { 238 } catch (SecurityException ignored) { 239 } finally { 240 try { 241 if (is != null) { 242 is.close(); 243 } 244 } catch (IOException e1) { 245 } 246 } 247 } 248 getPreference(String key)249 public static String getPreference(String key) { 250 return getPreferences().getProperty(key); 251 } 252 getPreference(String key, int dflt)253 public static int getPreference(String key, int dflt) { 254 String value = getPreference(key); 255 int intValue = dflt; 256 if (value == null) { 257 return intValue; 258 } 259 try { 260 intValue = Integer.parseInt(value); 261 } catch (NumberFormatException ne) { 262 } 263 return intValue; 264 } 265 266 /** 267 * Returns a filtered stack trace 268 */ getFilteredTrace(Throwable e)269 public static String getFilteredTrace(Throwable e) { 270 return BaseTestRunner.getFilteredTrace(Throwables.getStacktrace(e)); 271 } 272 273 /** 274 * Filters stack frames from internal JUnit classes 275 */ getFilteredTrace(String stack)276 public static String getFilteredTrace(String stack) { 277 if (showStackRaw()) { 278 return stack; 279 } 280 281 StringWriter sw = new StringWriter(); 282 PrintWriter pw = new PrintWriter(sw); 283 StringReader sr = new StringReader(stack); 284 BufferedReader br = new BufferedReader(sr); 285 286 String line; 287 try { 288 while ((line = br.readLine()) != null) { 289 if (!filterLine(line)) { 290 pw.println(line); 291 } 292 } 293 } catch (Exception IOException) { 294 return stack; // return the stack unfiltered 295 } 296 return sw.toString(); 297 } 298 showStackRaw()299 protected static boolean showStackRaw() { 300 return !getPreference("filterstack").equals("true") || fgFilterStack == false; 301 } 302 filterLine(String line)303 static boolean filterLine(String line) { 304 String[] patterns = new String[]{ 305 "junit.framework.TestCase", 306 "junit.framework.TestResult", 307 "junit.framework.TestSuite", 308 "junit.framework.Assert.", // don't filter AssertionFailure 309 "junit.swingui.TestRunner", 310 "junit.awtui.TestRunner", 311 "junit.textui.TestRunner", 312 "java.lang.reflect.Method.invoke(" 313 }; 314 for (int i = 0; i < patterns.length; i++) { 315 if (line.indexOf(patterns[i]) > 0) { 316 return true; 317 } 318 } 319 return false; 320 } 321 322 static { 323 fgMaxMessageLength = getPreference("maxmessage", fgMaxMessageLength); 324 } 325 326 } 327