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