001package junit.framework;
002
003import java.lang.reflect.Constructor;
004import java.lang.reflect.InvocationTargetException;
005import java.lang.reflect.Method;
006import java.lang.reflect.Modifier;
007import java.util.ArrayList;
008import java.util.Enumeration;
009import java.util.List;
010import java.util.Vector;
011
012import org.junit.internal.MethodSorter;
013import 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 */
043public 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}