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 }