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