001 package junit.runner;
002
003 import java.io.BufferedReader;
004 import java.io.File;
005 import java.io.FileInputStream;
006 import java.io.FileOutputStream;
007 import java.io.IOException;
008 import java.io.InputStream;
009 import java.io.PrintWriter;
010 import java.io.StringReader;
011 import java.io.StringWriter;
012 import java.lang.reflect.InvocationTargetException;
013 import java.lang.reflect.Method;
014 import java.lang.reflect.Modifier;
015 import java.text.NumberFormat;
016 import java.util.Properties;
017
018 import junit.framework.AssertionFailedError;
019 import junit.framework.Test;
020 import junit.framework.TestListener;
021 import junit.framework.TestSuite;
022
023 import 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 */
029 public 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 }