Paul Duffin | eef35dd | 2016-12-12 12:22:31 +0000 | [diff] [blame] | 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 | */ |
| 38 | public synchronized void startTest(Test test) { |
| 39 | testStarted(test.toString()); |
| 40 | } |
| 41 | |
| 42 | protected static void setPreferences(Properties preferences) { |
| 43 | fPreferences= preferences; |
| 44 | } |
| 45 | |
| 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 | |
| 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 | |
| 65 | // android-changed remove 'static' qualifier for API compatibility |
| 66 | public void setPreference(String key, String value) { |
| 67 | getPreferences().put(key, value); |
| 68 | } |
| 69 | |
| 70 | public synchronized void endTest(Test test) { |
| 71 | testEnded(test.toString()); |
| 72 | } |
| 73 | |
| 74 | public synchronized void addError(final Test test, final Throwable t) { |
| 75 | testFailed(TestRunListener.STATUS_ERROR, test, t); |
| 76 | } |
| 77 | |
| 78 | public synchronized void addFailure(final Test test, final AssertionFailedError t) { |
| 79 | testFailed(TestRunListener.STATUS_FAILURE, test, t); |
| 80 | } |
| 81 | |
| 82 | // TestRunListener implementation |
| 83 | |
| 84 | public abstract void testStarted(String testName); |
| 85 | |
| 86 | public abstract void testEnded(String testName); |
| 87 | |
| 88 | public abstract void testFailed(int status, Test test, Throwable t); |
| 89 | |
| 90 | /** |
| 91 | * Returns the Test corresponding to the given suite. This is |
| 92 | * a template method, subclasses override runFailed(), clearStatus(). |
| 93 | */ |
| 94 | public Test getTest(String suiteClassName) { |
| 95 | if (suiteClassName.length() <= 0) { |
| 96 | clearStatus(); |
| 97 | return null; |
| 98 | } |
| 99 | Class<?> testClass= null; |
| 100 | try { |
| 101 | testClass= loadSuiteClass(suiteClassName); |
| 102 | } catch (ClassNotFoundException e) { |
| 103 | String clazz= e.getMessage(); |
| 104 | if (clazz == null) |
| 105 | clazz= suiteClassName; |
| 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, new Class[0]); |
| 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, (Object[])new Class[0]); // 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 | } |
| 134 | catch (IllegalAccessException e) { |
| 135 | runFailed("Failed to invoke suite():" + e.toString()); |
| 136 | return null; |
| 137 | } |
| 138 | |
| 139 | clearStatus(); |
| 140 | return test; |
| 141 | } |
| 142 | |
| 143 | /** |
| 144 | * Returns the formatted string of the elapsed time. |
| 145 | */ |
| 146 | public String elapsedTimeAsString(long runTime) { |
| 147 | return NumberFormat.getInstance().format((double)runTime/1000); |
| 148 | } |
| 149 | |
| 150 | /** |
| 151 | * Processes the command line arguments and |
| 152 | * returns the name of the suite class to run or null |
| 153 | */ |
| 154 | protected String processArguments(String[] args) { |
| 155 | String suiteName= null; |
| 156 | for (int i= 0; i < args.length; i++) { |
| 157 | if (args[i].equals("-noloading")) { |
| 158 | setLoading(false); |
| 159 | } else if (args[i].equals("-nofilterstack")) { |
| 160 | fgFilterStack= false; |
| 161 | } else if (args[i].equals("-c")) { |
| 162 | if (args.length > i+1) |
| 163 | suiteName= extractClassName(args[i+1]); |
| 164 | else |
| 165 | System.out.println("Missing Test class name"); |
| 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 | */ |
| 177 | public void setLoading(boolean enable) { |
| 178 | fLoading= enable; |
| 179 | } |
| 180 | /** |
| 181 | * Extract the class name from a String in VA/Java style |
| 182 | */ |
| 183 | public String extractClassName(String className) { |
| 184 | if(className.startsWith("Default package for")) |
| 185 | return className.substring(className.lastIndexOf(".")+1); |
| 186 | return className; |
| 187 | } |
| 188 | |
| 189 | /** |
| 190 | * Truncates a String to the maximum length. |
| 191 | */ |
| 192 | public static String truncate(String s) { |
| 193 | if (fgMaxMessageLength != -1 && s.length() > fgMaxMessageLength) |
| 194 | s= s.substring(0, fgMaxMessageLength)+"..."; |
| 195 | return s; |
| 196 | } |
| 197 | |
| 198 | /** |
| 199 | * Override to define how to handle a failed loading of |
| 200 | * a test suite. |
| 201 | */ |
| 202 | protected abstract void runFailed(String message); |
| 203 | |
| 204 | // BEGIN android-changed - add back getLoader() for API compatibility |
| 205 | /** |
| 206 | * Returns the loader to be used. |
| 207 | * |
| 208 | * @deprecated not present in JUnit4.10 |
| 209 | */ |
| 210 | public TestSuiteLoader getLoader() { |
| 211 | return new StandardTestSuiteLoader(); |
| 212 | } |
| 213 | // END android-changed |
| 214 | |
| 215 | /** |
| 216 | * Returns the loaded Class for a suite name. |
| 217 | */ |
| 218 | protected Class<?> loadSuiteClass(String suiteClassName) throws ClassNotFoundException { |
| 219 | return Class.forName(suiteClassName); |
| 220 | } |
| 221 | |
| 222 | /** |
| 223 | * Clears the status message. |
| 224 | */ |
| 225 | protected void clearStatus() { // Belongs in the GUI TestRunner class |
| 226 | } |
| 227 | |
| 228 | protected boolean useReloadingTestSuiteLoader() { |
| 229 | return getPreference("loading").equals("true") && fLoading; |
| 230 | } |
| 231 | |
| 232 | private static File getPreferencesFile() { |
| 233 | String home= System.getProperty("user.home"); |
| 234 | return new File(home, "junit.properties"); |
| 235 | } |
| 236 | |
| 237 | private static void readPreferences() { |
| 238 | InputStream is= null; |
| 239 | try { |
| 240 | is= new FileInputStream(getPreferencesFile()); |
| 241 | setPreferences(new Properties(getPreferences())); |
| 242 | getPreferences().load(is); |
| 243 | } catch (IOException e) { |
| 244 | try { |
| 245 | if (is != null) |
| 246 | is.close(); |
| 247 | } catch (IOException e1) { |
| 248 | } |
| 249 | } |
| 250 | } |
| 251 | |
| 252 | public static String getPreference(String key) { |
| 253 | return getPreferences().getProperty(key); |
| 254 | } |
| 255 | |
| 256 | public static int getPreference(String key, int dflt) { |
| 257 | String value= getPreference(key); |
| 258 | int intValue= dflt; |
| 259 | if (value == null) |
| 260 | return intValue; |
| 261 | try { |
| 262 | intValue= Integer.parseInt(value); |
| 263 | } catch (NumberFormatException ne) { |
| 264 | } |
| 265 | return intValue; |
| 266 | } |
| 267 | |
| 268 | /** |
| 269 | * Returns a filtered stack trace |
| 270 | */ |
| 271 | public static String getFilteredTrace(Throwable t) { |
| 272 | StringWriter stringWriter= new StringWriter(); |
| 273 | PrintWriter writer= new PrintWriter(stringWriter); |
| 274 | t.printStackTrace(writer); |
| 275 | StringBuffer buffer= stringWriter.getBuffer(); |
| 276 | String trace= buffer.toString(); |
| 277 | return BaseTestRunner.getFilteredTrace(trace); |
| 278 | } |
| 279 | |
| 280 | // BEGIN android-changed - add back this method for API compatibility |
| 281 | /** @deprecated not present in JUnit4.10 */ |
| 282 | public static boolean inVAJava() { |
| 283 | return false; |
| 284 | } |
| 285 | // END android-changed |
| 286 | |
| 287 | /** |
| 288 | * Filters stack frames from internal JUnit classes |
| 289 | */ |
| 290 | public static String getFilteredTrace(String stack) { |
| 291 | if (showStackRaw()) |
| 292 | return stack; |
| 293 | |
| 294 | StringWriter sw= new StringWriter(); |
| 295 | PrintWriter pw= new PrintWriter(sw); |
| 296 | StringReader sr= new StringReader(stack); |
| 297 | // BEGIN android-changed |
| 298 | // Use a sensible default buffer size |
| 299 | BufferedReader br= new BufferedReader(sr, 1000); |
| 300 | // END android-changed |
| 301 | |
| 302 | String line; |
| 303 | try { |
| 304 | while ((line= br.readLine()) != null) { |
| 305 | if (!filterLine(line)) |
| 306 | pw.println(line); |
| 307 | } |
| 308 | } catch (Exception IOException) { |
| 309 | return stack; // return the stack unfiltered |
| 310 | } |
| 311 | return sw.toString(); |
| 312 | } |
| 313 | |
| 314 | protected static boolean showStackRaw() { |
| 315 | return !getPreference("filterstack").equals("true") || fgFilterStack == false; |
| 316 | } |
| 317 | |
| 318 | static boolean filterLine(String line) { |
| 319 | String[] patterns= new String[] { |
| 320 | "junit.framework.TestCase", |
| 321 | "junit.framework.TestResult", |
| 322 | "junit.framework.TestSuite", |
| 323 | "junit.framework.Assert.", // don't filter AssertionFailure |
| 324 | "junit.swingui.TestRunner", |
| 325 | "junit.awtui.TestRunner", |
| 326 | "junit.textui.TestRunner", |
| 327 | "java.lang.reflect.Method.invoke(" |
| 328 | }; |
| 329 | for (int i= 0; i < patterns.length; i++) { |
| 330 | if (line.indexOf(patterns[i]) > 0) |
| 331 | return true; |
| 332 | } |
| 333 | return false; |
| 334 | } |
| 335 | |
| 336 | static { |
| 337 | fgMaxMessageLength= getPreference("maxmessage", fgMaxMessageLength); |
| 338 | } |
| 339 | |
| 340 | } |