Home | History | Annotate | Download | only in framework
      1 package junit.framework;
      2 
      3 import java.io.PrintWriter;
      4 import java.io.StringWriter;
      5 import java.lang.reflect.Constructor;
      6 import java.lang.reflect.InvocationTargetException;
      7 import java.lang.reflect.Method;
      8 import java.lang.reflect.Modifier;
      9 import java.util.ArrayList;
     10 import java.util.Enumeration;
     11 import java.util.List;
     12 import java.util.Vector;
     13 
     14 import org.junit.internal.MethodSorter;
     15 
     16 /**
     17  * A <code>TestSuite</code> is a <code>Composite</code> of Tests.
     18  * It runs a collection of test cases. Here is an example using
     19  * the dynamic test definition.
     20  * <pre>
     21  * TestSuite suite= new TestSuite();
     22  * suite.addTest(new MathTest("testAdd"));
     23  * suite.addTest(new MathTest("testDivideByZero"));
     24  * </pre>
     25  * <p>
     26  * Alternatively, a TestSuite can extract the tests to be run automatically.
     27  * To do so you pass the class of your TestCase class to the
     28  * TestSuite constructor.
     29  * <pre>
     30  * TestSuite suite= new TestSuite(MathTest.class);
     31  * </pre>
     32  * <p>
     33  * This constructor creates a suite with all the methods
     34  * starting with "test" that take no arguments.
     35  * <p>
     36  * A final option is to do the same for a large array of test classes.
     37  * <pre>
     38  * Class[] testClasses = { MathTest.class, AnotherTest.class }
     39  * TestSuite suite= new TestSuite(testClasses);
     40  * </pre>
     41  *
     42  * @see Test
     43  */
     44 public class TestSuite implements Test {
     45 
     46     /**
     47      * ...as the moon sets over the early morning Merlin, Oregon
     48      * mountains, our intrepid adventurers type...
     49      */
     50     static public Test createTest(Class<?> theClass, String name) {
     51         Constructor<?> constructor;
     52         try {
     53             constructor = getTestConstructor(theClass);
     54         } catch (NoSuchMethodException e) {
     55             return warning("Class " + theClass.getName() + " has no public constructor TestCase(String name) or TestCase()");
     56         }
     57         Object test;
     58         try {
     59             if (constructor.getParameterTypes().length == 0) {
     60                 test = constructor.newInstance(new Object[0]);
     61                 if (test instanceof TestCase) {
     62                     ((TestCase) test).setName(name);
     63                 }
     64             } else {
     65                 test = constructor.newInstance(new Object[]{name});
     66             }
     67         } catch (InstantiationException e) {
     68             return (warning("Cannot instantiate test case: " + name + " (" + exceptionToString(e) + ")"));
     69         } catch (InvocationTargetException e) {
     70             return (warning("Exception in constructor: " + name + " (" + exceptionToString(e.getTargetException()) + ")"));
     71         } catch (IllegalAccessException e) {
     72             return (warning("Cannot access test case: " + name + " (" + exceptionToString(e) + ")"));
     73         }
     74         return (Test) test;
     75     }
     76 
     77     /**
     78      * Gets a constructor which takes a single String as
     79      * its argument or a no arg constructor.
     80      */
     81     public static Constructor<?> getTestConstructor(Class<?> theClass) throws NoSuchMethodException {
     82         try {
     83             return theClass.getConstructor(String.class);
     84         } catch (NoSuchMethodException e) {
     85             // fall through
     86         }
     87         return theClass.getConstructor();
     88     }
     89 
     90     /**
     91      * Returns a test which will fail and log a warning message.
     92      */
     93     public static Test warning(final String message) {
     94         return new TestCase("warning") {
     95             @Override
     96             protected void runTest() {
     97                 fail(message);
     98             }
     99         };
    100     }
    101 
    102     /**
    103      * Converts the stack trace into a string
    104      */
    105     private static String exceptionToString(Throwable e) {
    106         StringWriter stringWriter = new StringWriter();
    107         PrintWriter writer = new PrintWriter(stringWriter);
    108         e.printStackTrace(writer);
    109         return stringWriter.toString();
    110     }
    111 
    112     private String fName;
    113 
    114     private Vector<Test> fTests = new Vector<Test>(10); // Cannot convert this to List because it is used directly by some test runners
    115 
    116     /**
    117      * Constructs an empty TestSuite.
    118      */
    119     public TestSuite() {
    120     }
    121 
    122     /**
    123      * Constructs a TestSuite from the given class. Adds all the methods
    124      * starting with "test" as test cases to the suite.
    125      * Parts of this method were written at 2337 meters in the Hueffihuette,
    126      * Kanton Uri
    127      */
    128     public TestSuite(final Class<?> theClass) {
    129         addTestsFromTestCase(theClass);
    130     }
    131 
    132     private void addTestsFromTestCase(final Class<?> theClass) {
    133         fName = theClass.getName();
    134         try {
    135             getTestConstructor(theClass); // Avoid generating multiple error messages
    136         } catch (NoSuchMethodException e) {
    137             addTest(warning("Class " + theClass.getName() + " has no public constructor TestCase(String name) or TestCase()"));
    138             return;
    139         }
    140 
    141         if (!Modifier.isPublic(theClass.getModifiers())) {
    142             addTest(warning("Class " + theClass.getName() + " is not public"));
    143             return;
    144         }
    145 
    146         Class<?> superClass = theClass;
    147         List<String> names = new ArrayList<String>();
    148         while (Test.class.isAssignableFrom(superClass)) {
    149             for (Method each : MethodSorter.getDeclaredMethods(superClass)) {
    150                 addTestMethod(each, names, theClass);
    151             }
    152             superClass = superClass.getSuperclass();
    153         }
    154         if (fTests.size() == 0) {
    155             addTest(warning("No tests found in " + theClass.getName()));
    156         }
    157     }
    158 
    159     /**
    160      * Constructs a TestSuite from the given class with the given name.
    161      *
    162      * @see TestSuite#TestSuite(Class)
    163      */
    164     public TestSuite(Class<? extends TestCase> theClass, String name) {
    165         this(theClass);
    166         setName(name);
    167     }
    168 
    169     /**
    170      * Constructs an empty TestSuite.
    171      */
    172     public TestSuite(String name) {
    173         setName(name);
    174     }
    175 
    176     /**
    177      * Constructs a TestSuite from the given array of classes.
    178      *
    179      * @param classes {@link TestCase}s
    180      */
    181     public TestSuite(Class<?>... classes) {
    182         for (Class<?> each : classes) {
    183             addTest(testCaseForClass(each));
    184         }
    185     }
    186 
    187     private Test testCaseForClass(Class<?> each) {
    188         if (TestCase.class.isAssignableFrom(each)) {
    189             return new TestSuite(each.asSubclass(TestCase.class));
    190         } else {
    191             return warning(each.getCanonicalName() + " does not extend TestCase");
    192         }
    193     }
    194 
    195     /**
    196      * Constructs a TestSuite from the given array of classes with the given name.
    197      *
    198      * @see TestSuite#TestSuite(Class[])
    199      */
    200     public TestSuite(Class<? extends TestCase>[] classes, String name) {
    201         this(classes);
    202         setName(name);
    203     }
    204 
    205     /**
    206      * Adds a test to the suite.
    207      */
    208     public void addTest(Test test) {
    209         fTests.add(test);
    210     }
    211 
    212     /**
    213      * Adds the tests from the given class to the suite
    214      */
    215     public void addTestSuite(Class<? extends TestCase> testClass) {
    216         addTest(new TestSuite(testClass));
    217     }
    218 
    219     /**
    220      * Counts the number of test cases that will be run by this test.
    221      */
    222     public int countTestCases() {
    223         int count = 0;
    224         for (Test each : fTests) {
    225             count += each.countTestCases();
    226         }
    227         return count;
    228     }
    229 
    230     /**
    231      * Returns the name of the suite. Not all
    232      * test suites have a name and this method
    233      * can return null.
    234      */
    235     public String getName() {
    236         return fName;
    237     }
    238 
    239     /**
    240      * Runs the tests and collects their result in a TestResult.
    241      */
    242     public void run(TestResult result) {
    243         for (Test each : fTests) {
    244             if (result.shouldStop()) {
    245                 break;
    246             }
    247             runTest(each, result);
    248         }
    249     }
    250 
    251     public void runTest(Test test, TestResult result) {
    252         test.run(result);
    253     }
    254 
    255     /**
    256      * Sets the name of the suite.
    257      *
    258      * @param name the name to set
    259      */
    260     public void setName(String name) {
    261         fName = name;
    262     }
    263 
    264     /**
    265      * Returns the test at the given index
    266      */
    267     public Test testAt(int index) {
    268         return fTests.get(index);
    269     }
    270 
    271     /**
    272      * Returns the number of tests in this suite
    273      */
    274     public int testCount() {
    275         return fTests.size();
    276     }
    277 
    278     /**
    279      * Returns the tests as an enumeration
    280      */
    281     public Enumeration<Test> tests() {
    282         return fTests.elements();
    283     }
    284 
    285     /**
    286      */
    287     @Override
    288     public String toString() {
    289         if (getName() != null) {
    290             return getName();
    291         }
    292         return super.toString();
    293     }
    294 
    295     private void addTestMethod(Method m, List<String> names, Class<?> theClass) {
    296         String name = m.getName();
    297         if (names.contains(name)) {
    298             return;
    299         }
    300         if (!isPublicTestMethod(m)) {
    301             if (isTestMethod(m)) {
    302                 addTest(warning("Test method isn't public: " + m.getName() + "(" + theClass.getCanonicalName() + ")"));
    303             }
    304             return;
    305         }
    306         names.add(name);
    307         addTest(createTest(theClass, name));
    308     }
    309 
    310     private boolean isPublicTestMethod(Method m) {
    311         return isTestMethod(m) && Modifier.isPublic(m.getModifiers());
    312     }
    313 
    314     private boolean isTestMethod(Method m) {
    315         return m.getParameterTypes().length == 0 &&
    316                 m.getName().startsWith("test") &&
    317                 m.getReturnType().equals(Void.TYPE);
    318     }
    319 }
    320