001    package junit.runner;
002    
003    import junit.framework.*;
004    import java.lang.reflect.*;
005    import java.text.NumberFormat;
006    import java.io.*;
007    import java.util.*;
008    
009    /**
010     * Base class for all test runners.
011     * This class was born live on stage in Sardinia during XP2000.
012     */
013    public abstract class BaseTestRunner implements TestListener {
014            public static final String SUITE_METHODNAME= "suite";
015    
016            private static Properties fPreferences;
017            static int fgMaxMessageLength= 500;
018            static boolean fgFilterStack= true;
019            boolean fLoading= true;
020    
021        /*
022        * Implementation of TestListener
023        */
024            public synchronized void startTest(Test test) {
025                    testStarted(test.toString());
026            }
027    
028            protected static void setPreferences(Properties preferences) {
029                    fPreferences= preferences;
030            }
031    
032            protected static Properties getPreferences() {
033                    if (fPreferences == null) {
034                            fPreferences= new Properties();
035                            fPreferences.put("loading", "true");
036                            fPreferences.put("filterstack", "true");
037                            readPreferences();
038                    }
039                    return fPreferences;
040            }
041    
042            public static void savePreferences() throws IOException {
043                    FileOutputStream fos= new FileOutputStream(getPreferencesFile());
044                    try {
045                            getPreferences().store(fos, "");
046                    } finally {
047                            fos.close();
048                    }
049            }
050    
051            public void setPreference(String key, String value) {
052                    getPreferences().setProperty(key, value);
053            }
054    
055            public synchronized void endTest(Test test) {
056                    testEnded(test.toString());
057            }
058    
059            public synchronized void addError(final Test test, final Throwable t) {
060                    testFailed(TestRunListener.STATUS_ERROR, test, t);
061            }
062    
063            public synchronized void addFailure(final Test test, final AssertionFailedError t) {
064                    testFailed(TestRunListener.STATUS_FAILURE, test, t);
065            }
066    
067            // TestRunListener implementation
068    
069            public abstract void testStarted(String testName);
070    
071            public abstract void testEnded(String testName);
072    
073            public abstract void testFailed(int status, Test test, Throwable t);
074    
075            /**
076             * Returns the Test corresponding to the given suite. This is
077             * a template method, subclasses override runFailed(), clearStatus().
078             */
079            public Test getTest(String suiteClassName) {
080                    if (suiteClassName.length() <= 0) {
081                            clearStatus();
082                            return null;
083                    }
084                    Class testClass= null;
085                    try {
086                            testClass= loadSuiteClass(suiteClassName);
087                    } catch (ClassNotFoundException e) {
088                            String clazz= e.getMessage();
089                            if (clazz == null)
090                                    clazz= suiteClassName;
091                            runFailed("Class not found \""+clazz+"\"");
092                            return null;
093                    } catch(Exception e) {
094                            runFailed("Error: "+e.toString());
095                            return null;
096                    }
097                    Method suiteMethod= null;
098                    try {
099                            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            /**
262             * Returns a filtered stack trace
263             */
264            public static String getFilteredTrace(Throwable t) {
265                    StringWriter stringWriter= new StringWriter();
266                    PrintWriter writer= new PrintWriter(stringWriter);
267                    t.printStackTrace(writer);
268                    StringBuffer buffer= stringWriter.getBuffer();
269                    String trace= buffer.toString();
270                    return BaseTestRunner.getFilteredTrace(trace);
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                    StringWriter sw= new StringWriter();
281                    PrintWriter pw= new PrintWriter(sw);
282                    StringReader sr= new StringReader(stack);
283                    BufferedReader br= new BufferedReader(sr);
284    
285                    String line;
286                    try {
287                            while ((line= br.readLine()) != null) {
288                                    if (!filterLine(line))
289                                            pw.println(line);
290                            }
291                    } catch (Exception IOException) {
292                            return stack; // return the stack unfiltered
293                    }
294                    return sw.toString();
295            }
296    
297            protected static boolean showStackRaw() {
298                    return !getPreference("filterstack").equals("true") || fgFilterStack == false;
299            }
300    
301            static boolean filterLine(String line) {
302                    String[] patterns= new String[] {
303                            "junit.framework.TestCase",
304                            "junit.framework.TestResult",
305                            "junit.framework.TestSuite",
306                            "junit.framework.Assert.", // don't filter AssertionFailure
307                            "junit.swingui.TestRunner",
308                            "junit.awtui.TestRunner",
309                            "junit.textui.TestRunner",
310                            "java.lang.reflect.Method.invoke("
311                    };
312                    for (int i= 0; i < patterns.length; i++) {
313                            if (line.indexOf(patterns[i]) > 0)
314                                    return true;
315                    }
316                    return false;
317            }
318    
319            static {
320                    fgMaxMessageLength= getPreference("maxmessage", fgMaxMessageLength);
321            }
322    
323    }