    001    package junit.framework;
    002
    003    import java.lang.reflect.Constructor;
    004    import java.lang.reflect.InvocationTargetException;
    005    import java.lang.reflect.Method;
    006    import java.lang.reflect.Modifier;
    007    import java.util.ArrayList;
    008    import java.util.Enumeration;
    009    import java.util.List;
    010    import java.util.Vector;
    011
    012    import org.junit.internal.MethodSorter;
    013    import org.junit.internal.Throwables;
    014
    015    /**
    016     * A <code>TestSuite</code> is a <code>Composite</code> of Tests.
    017     * It runs a collection of test cases. Here is an example using
    018     * the dynamic test definition.
    019     * <pre>
    020     * TestSuite suite= new TestSuite();
    021     * suite.addTest(new MathTest("testAdd"));
    022     * suite.addTest(new MathTest("testDivideByZero"));
    023     * </pre>
    024     * <p>
    025     * Alternatively, a TestSuite can extract the tests to be run automatically.
    026     * To do so you pass the class of your TestCase class to the
    027     * TestSuite constructor.
    028     * <pre>
    029     * TestSuite suite= new TestSuite(MathTest.class);
    030     * </pre>
    031     * <p>
    032     * This constructor creates a suite with all the methods
    033     * starting with "test" that take no arguments.
    034     * <p>
    035     * A final option is to do the same for a large array of test classes.
    036     * <pre>
    037     * Class[] testClasses = { MathTest.class, AnotherTest.class };
    038     * TestSuite suite= new TestSuite(testClasses);
    039     * </pre>
    040     *
    041     * @see Test
    042     */
    043    public class TestSuite implements Test {
    044
    045        /**
    046         * ...as the moon sets over the early morning Merlin, Oregon
    047         * mountains, our intrepid adventurers type...
    048         */
    049        static public Test createTest(Class<?> theClass, String name) {
    050            Constructor<?> constructor;
    051            try {
    052                constructor = getTestConstructor(theClass);
    053            } catch (NoSuchMethodException e) {
    054                return warning("Class " + theClass.getName() + " has no public constructor TestCase(String name) or TestCase()");
    055            }
    056            Object test;
    057            try {
    058                if (constructor.getParameterTypes().length == 0) {
    059                    test = constructor.newInstance(new Object[0]);
    060                    if (test instanceof TestCase) {
    061                        ((TestCase) test).setName(name);
    062                    }
    063                } else {
    064                    test = constructor.newInstance(new Object[]{name});
    065                }
    066            } catch (InstantiationException e) {
    067                return (warning("Cannot instantiate test case: " + name + " (" + Throwables.getStacktrace(e) + ")"));
    068            } catch (InvocationTargetException e) {
    069                return (warning("Exception in constructor: " + name + " (" + Throwables.getStacktrace(e.getTargetException()) + ")"));
    070            } catch (IllegalAccessException e) {
    071                return (warning("Cannot access test case: " + name + " (" + Throwables.getStacktrace(e) + ")"));
    072            }
    073            return (Test) test;
    074        }
    075
    076        /**
    077         * Gets a constructor which takes a single String as
    078         * its argument or a no arg constructor.
    079         */
    080        public static Constructor<?> getTestConstructor(Class<?> theClass) throws NoSuchMethodException {
    081            try {
    082                return theClass.getConstructor(String.class);
    083            } catch (NoSuchMethodException e) {
    084                // fall through
    085            }
    086            return theClass.getConstructor();
    087        }
    088
    089        /**
    090         * Returns a test which will fail and log a warning message.
    091         */
    092        public static Test warning(final String message) {
    093            return new TestCase("warning") {
    094                @Override
    095                protected void runTest() {
    096                    fail(message);
    097                }
    098            };
    099        }
    100
    101        private String fName;
    102
    103        private Vector<Test> fTests = new Vector<Test>(10); // Cannot convert this to List because it is used directly by some test runners
    104
    105        /**
    106         * Constructs an empty TestSuite.
    107         */
    108        public TestSuite() {
    109        }
    110
    111        /**
    112         * Constructs a TestSuite from the given class. Adds all the methods
    113         * starting with "test" as test cases to the suite.
    114         * Parts of this method were written at 2337 meters in the Hueffihuette,
    115         * Kanton Uri
    116         */
    117        public TestSuite(final Class<?> theClass) {
    118            addTestsFromTestCase(theClass);
    119        }
    120
    121        private void addTestsFromTestCase(final Class<?> theClass) {
    122            fName = theClass.getName();
    123            try {
    124                getTestConstructor(theClass); // Avoid generating multiple error messages
    125            } catch (NoSuchMethodException e) {
    126                addTest(warning("Class " + theClass.getName() + " has no public constructor TestCase(String name) or TestCase()"));
    127                return;
    128            }
    129
    130            if (!Modifier.isPublic(theClass.getModifiers())) {
    131                addTest(warning("Class " + theClass.getName() + " is not public"));
    132                return;
    133            }
    134
    135            Class<?> superClass = theClass;
    136            List<String> names = new ArrayList<String>();
    137            while (Test.class.isAssignableFrom(superClass)) {
    138                for (Method each : MethodSorter.getDeclaredMethods(superClass)) {
    139                    addTestMethod(each, names, theClass);
    140                }
    141                superClass = superClass.getSuperclass();
    142            }
    143            if (fTests.size() == 0) {
    144                addTest(warning("No tests found in " + theClass.getName()));
    145            }
    146        }
    147
    148        /**
    149         * Constructs a TestSuite from the given class with the given name.
    150         *
    151         * @see TestSuite#TestSuite(Class)
    152         */
    153        public TestSuite(Class<? extends TestCase> theClass, String name) {
    154            this(theClass);
    155            setName(name);
    156        }
    157
    158        /**
    159         * Constructs an empty TestSuite.
    160         */
    161        public TestSuite(String name) {
    162            setName(name);
    163        }
    164
    165        /**
    166         * Constructs a TestSuite from the given array of classes.
    167         *
    168         * @param classes {@link TestCase}s
    169         */
    170        public TestSuite(Class<?>... classes) {
    171            for (Class<?> each : classes) {
    172                addTest(testCaseForClass(each));
    173            }
    174        }
    175
    176        private Test testCaseForClass(Class<?> each) {
    177            if (TestCase.class.isAssignableFrom(each)) {
    178                return new TestSuite(each.asSubclass(TestCase.class));
    179            } else {
    180                return warning(each.getCanonicalName() + " does not extend TestCase");
    181            }
    182        }
    183
    184        /**
    185         * Constructs a TestSuite from the given array of classes with the given name.
    186         *
    187         * @see TestSuite#TestSuite(Class[])
    188         */
    189        public TestSuite(Class<? extends TestCase>[] classes, String name) {
    190            this(classes);
    191            setName(name);
    192        }
    193
    194        /**
    195         * Adds a test to the suite.
    196         */
    197        public void addTest(Test test) {
    198            fTests.add(test);
    199        }
    200
    201        /**
    202         * Adds the tests from the given class to the suite.
    203         */
    204        public void addTestSuite(Class<? extends TestCase> testClass) {
    205            addTest(new TestSuite(testClass));
    206        }
    207
    208        /**
    209         * Counts the number of test cases that will be run by this test.
    210         */
    211        public int countTestCases() {
    212            int count = 0;
    213            for (Test each : fTests) {
    214                count += each.countTestCases();
    215            }
    216            return count;
    217        }
    218
    219        /**
    220         * Returns the name of the suite. Not all
    221         * test suites have a name and this method
    222         * can return null.
    223         */
    224        public String getName() {
    225            return fName;
    226        }
    227
    228        /**
    229         * Runs the tests and collects their result in a TestResult.
    230         */
    231        public void run(TestResult result) {
    232            for (Test each : fTests) {
    233                if (result.shouldStop()) {
    234                    break;
    235                }
    236                runTest(each, result);
    237            }
    238        }
    239
    240        public void runTest(Test test, TestResult result) {
    241            test.run(result);
    242        }
    243
    244        /**
    245         * Sets the name of the suite.
    246         *
    247         * @param name the name to set
    248         */
    249        public void setName(String name) {
    250            fName = name;
    251        }
    252
    253        /**
    254         * Returns the test at the given index.
    255         */
    256        public Test testAt(int index) {
    257            return fTests.get(index);
    258        }
    259
    260        /**
    261         * Returns the number of tests in this suite.
    262         */
    263        public int testCount() {
    264            return fTests.size();
    265        }
    266
    267        /**
    268         * Returns the tests as an enumeration.
    269         */
    270        public Enumeration<Test> tests() {
    271            return fTests.elements();
    272        }
    273
    274        /**
    275         */
    276        @Override
    277        public String toString() {
    278            if (getName() != null) {
    279                return getName();
    280            }
    281            return super.toString();
    282        }
    283
    284        private void addTestMethod(Method m, List<String> names, Class<?> theClass) {
    285            String name = m.getName();
    286            if (names.contains(name)) {
    287                return;
    288            }
    289            if (!isPublicTestMethod(m)) {
    290                if (isTestMethod(m)) {
    291                    addTest(warning("Test method isn't public: " + m.getName() + "(" + theClass.getCanonicalName() + ")"));
    292                }
    293                return;
    294            }
    295            names.add(name);
    296            addTest(createTest(theClass, name));
    297        }
    298
    299        private boolean isPublicTestMethod(Method m) {
    300            return isTestMethod(m) && Modifier.isPublic(m.getModifiers());
    301        }
    302
    303        private boolean isTestMethod(Method m) {
    304            return m.getParameterTypes().length == 0 &&
    305                    m.getName().startsWith("test") &&
    306                    m.getReturnType().equals(Void.TYPE);
    307        }
    308    }