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