001package junit.runner; 002 003import java.io.BufferedReader; 004import java.io.File; 005import java.io.FileInputStream; 006import java.io.FileOutputStream; 007import java.io.IOException; 008import java.io.InputStream; 009import java.io.PrintWriter; 010import java.io.StringReader; 011import java.io.StringWriter; 012import java.lang.reflect.InvocationTargetException; 013import java.lang.reflect.Method; 014import java.lang.reflect.Modifier; 015import java.text.NumberFormat; 016import java.util.Properties; 017 018import junit.framework.AssertionFailedError; 019import junit.framework.Test; 020import junit.framework.TestListener; 021import junit.framework.TestSuite; 022 023import org.junit.internal.Throwables; 024 025/** 026 * Base class for all test runners. 027 * This class was born live on stage in Sardinia during XP2000. 028 */ 029public abstract class BaseTestRunner implements TestListener { 030 public static final String SUITE_METHODNAME = "suite"; 031 032 private static Properties fPreferences; 033 static int fgMaxMessageLength = 500; 034 static boolean fgFilterStack = true; 035 boolean fLoading = true; 036 037 /* 038 * Implementation of TestListener 039 */ 040 public synchronized void startTest(Test test) { 041 testStarted(test.toString()); 042 } 043 044 protected static void setPreferences(Properties preferences) { 045 fPreferences = preferences; 046 } 047 048 protected static Properties getPreferences() { 049 if (fPreferences == null) { 050 fPreferences = new Properties(); 051 fPreferences.put("loading", "true"); 052 fPreferences.put("filterstack", "true"); 053 readPreferences(); 054 } 055 return fPreferences; 056 } 057 058 public static void savePreferences() throws IOException { 059 FileOutputStream fos = new FileOutputStream(getPreferencesFile()); 060 try { 061 getPreferences().store(fos, ""); 062 } finally { 063 fos.close(); 064 } 065 } 066 067 public static void setPreference(String key, String value) { 068 getPreferences().put(key, value); 069 } 070 071 public synchronized void endTest(Test test) { 072 testEnded(test.toString()); 073 } 074 075 public synchronized void addError(final Test test, final Throwable e) { 076 testFailed(TestRunListener.STATUS_ERROR, test, e); 077 } 078 079 public synchronized void addFailure(final Test test, final AssertionFailedError e) { 080 testFailed(TestRunListener.STATUS_FAILURE, test, e); 081 } 082 083 // TestRunListener implementation 084 085 public abstract void testStarted(String testName); 086 087 public abstract void testEnded(String testName); 088 089 public abstract void testFailed(int status, Test test, Throwable e); 090 091 /** 092 * Returns the Test corresponding to the given suite. This is 093 * a template method, subclasses override runFailed(), clearStatus(). 094 */ 095 public Test getTest(String suiteClassName) { 096 if (suiteClassName.length() <= 0) { 097 clearStatus(); 098 return null; 099 } 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 207 protected abstract void runFailed(String message); 208 209 /** 210 * Returns the loaded Class for a suite name. 211 */ 212 protected Class<?> loadSuiteClass(String suiteClassName) throws ClassNotFoundException { 213 return Class.forName(suiteClassName); 214 } 215 216 /** 217 * Clears the status message. 218 */ 219 protected void clearStatus() { // Belongs in the GUI TestRunner class 220 } 221 222 protected boolean useReloadingTestSuiteLoader() { 223 return getPreference("loading").equals("true") && fLoading; 224 } 225 226 private static File getPreferencesFile() { 227 String home = System.getProperty("user.home"); 228 return new File(home, "junit.properties"); 229 } 230 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 249 public static String getPreference(String key) { 250 return getPreferences().getProperty(key); 251 } 252 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 */ 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 */ 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 299 protected static boolean showStackRaw() { 300 return !getPreference("filterstack").equals("true") || fgFilterStack == false; 301 } 302 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}