• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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      */
createTest(Class<?> theClass, String name)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      */
getTestConstructor(Class<?> theClass)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      */
warning(final String message)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