|
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.TestCase; |
|
21 import junit.framework.TestListener; |
|
22 import junit.framework.TestSuite; |
|
23 |
|
24 /** |
|
25 * Base class for all test runners. |
|
26 * This class was born live on stage in Sardinia during XP2000. |
|
27 */ |
|
28 public abstract class BaseTestRunner implements TestListener { |
|
29 public static final String SUITE_METHODNAME= "suite"; |
|
30 |
|
31 private static Properties fPreferences; |
|
32 static int fgMaxMessageLength= 500; |
|
33 static boolean fgFilterStack= true; |
|
34 boolean fLoading= true; |
|
35 |
|
36 /* |
|
37 * Implementation of TestListener |
|
38 */ |
|
39 public synchronized void startTest(Test test) { |
|
40 testStarted(test.toString()); |
|
41 } |
|
42 |
|
43 protected static void setPreferences(Properties preferences) { |
|
44 fPreferences= preferences; |
|
45 } |
|
46 |
|
47 protected static Properties getPreferences() { |
|
48 if (fPreferences == null) { |
|
49 fPreferences= new Properties(); |
|
50 fPreferences.put("loading", "true"); |
|
51 fPreferences.put("filterstack", "true"); |
|
52 readPreferences(); |
|
53 } |
|
54 return fPreferences; |
|
55 } |
|
56 |
|
57 public static void savePreferences() throws IOException { |
|
58 FileOutputStream fos= new FileOutputStream(getPreferencesFile()); |
|
59 try { |
|
60 getPreferences().store(fos, ""); |
|
61 } finally { |
|
62 fos.close(); |
|
63 } |
|
64 } |
|
65 |
|
66 public static 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<? extends TestCase> 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 /** |
|
205 * Returns the loaded Class for a suite name. |
|
206 */ |
|
207 protected Class<? extends TestCase> loadSuiteClass(String suiteClassName) throws ClassNotFoundException { |
|
208 return Class.forName(suiteClassName).asSubclass(TestCase.class); |
|
209 } |
|
210 |
|
211 /** |
|
212 * Clears the status message. |
|
213 */ |
|
214 protected void clearStatus() { // Belongs in the GUI TestRunner class |
|
215 } |
|
216 |
|
217 protected boolean useReloadingTestSuiteLoader() { |
|
218 return getPreference("loading").equals("true") && fLoading; |
|
219 } |
|
220 |
|
221 private static File getPreferencesFile() { |
|
222 String home= System.getProperty("user.home"); |
|
223 return new File(home, "junit.properties"); |
|
224 } |
|
225 |
|
226 private static void readPreferences() { |
|
227 InputStream is= null; |
|
228 try { |
|
229 is= new FileInputStream(getPreferencesFile()); |
|
230 setPreferences(new Properties(getPreferences())); |
|
231 getPreferences().load(is); |
|
232 } catch (IOException e) { |
|
233 try { |
|
234 if (is != null) |
|
235 is.close(); |
|
236 } catch (IOException e1) { |
|
237 } |
|
238 } |
|
239 } |
|
240 |
|
241 public static String getPreference(String key) { |
|
242 return getPreferences().getProperty(key); |
|
243 } |
|
244 |
|
245 public static int getPreference(String key, int dflt) { |
|
246 String value= getPreference(key); |
|
247 int intValue= dflt; |
|
248 if (value == null) |
|
249 return intValue; |
|
250 try { |
|
251 intValue= Integer.parseInt(value); |
|
252 } catch (NumberFormatException ne) { |
|
253 } |
|
254 return intValue; |
|
255 } |
|
256 |
|
257 /** |
|
258 * Returns a filtered stack trace |
|
259 */ |
|
260 public static String getFilteredTrace(Throwable t) { |
|
261 StringWriter stringWriter= new StringWriter(); |
|
262 PrintWriter writer= new PrintWriter(stringWriter); |
|
263 t.printStackTrace(writer); |
|
264 StringBuffer buffer= stringWriter.getBuffer(); |
|
265 String trace= buffer.toString(); |
|
266 return BaseTestRunner.getFilteredTrace(trace); |
|
267 } |
|
268 |
|
269 /** |
|
270 * Filters stack frames from internal JUnit classes |
|
271 */ |
|
272 public static String getFilteredTrace(String stack) { |
|
273 if (showStackRaw()) |
|
274 return stack; |
|
275 |
|
276 StringWriter sw= new StringWriter(); |
|
277 PrintWriter pw= new PrintWriter(sw); |
|
278 StringReader sr= new StringReader(stack); |
|
279 BufferedReader br= new BufferedReader(sr); |
|
280 |
|
281 String line; |
|
282 try { |
|
283 while ((line= br.readLine()) != null) { |
|
284 if (!filterLine(line)) |
|
285 pw.println(line); |
|
286 } |
|
287 } catch (Exception IOException) { |
|
288 return stack; // return the stack unfiltered |
|
289 } |
|
290 return sw.toString(); |
|
291 } |
|
292 |
|
293 protected static boolean showStackRaw() { |
|
294 return !getPreference("filterstack").equals("true") || fgFilterStack == false; |
|
295 } |
|
296 |
|
297 static boolean filterLine(String line) { |
|
298 String[] patterns= new String[] { |
|
299 "junit.framework.TestCase", |
|
300 "junit.framework.TestResult", |
|
301 "junit.framework.TestSuite", |
|
302 "junit.framework.Assert.", // don't filter AssertionFailure |
|
303 "junit.swingui.TestRunner", |
|
304 "junit.awtui.TestRunner", |
|
305 "junit.textui.TestRunner", |
|
306 "java.lang.reflect.Method.invoke(" |
|
307 }; |
|
308 for (int i= 0; i < patterns.length; i++) { |
|
309 if (line.indexOf(patterns[i]) > 0) |
|
310 return true; |
|
311 } |
|
312 return false; |
|
313 } |
|
314 |
|
315 static { |
|
316 fgMaxMessageLength= getPreference("maxmessage", fgMaxMessageLength); |
|
317 } |
|
318 |
|
319 } |