blob: 336efd1800d7c4d3d897a6e9348e746e9483d4cc [file] [log] [blame]
Paul Duffincd7c34d2016-12-12 16:35:36 +00001package junit.framework;
2
3import java.io.PrintWriter;
4import java.io.StringWriter;
5import java.lang.reflect.Constructor;
6import java.lang.reflect.InvocationTargetException;
7import java.lang.reflect.Method;
8import java.lang.reflect.Modifier;
9import java.util.ArrayList;
10import java.util.Enumeration;
11import java.util.List;
12import java.util.Vector;
13
14/**
15 * <p>A <code>TestSuite</code> is a <code>Composite</code> of Tests.
16 * It runs a collection of test cases. Here is an example using
17 * the dynamic test definition.
18 * <pre>
19 * TestSuite suite= new TestSuite();
20 * suite.addTest(new MathTest("testAdd"));
21 * suite.addTest(new MathTest("testDivideByZero"));
22 * </pre>
23 * </p>
24 *
25 * <p>Alternatively, a TestSuite can extract the tests to be run automatically.
26 * To do so you pass the class of your TestCase class to the
27 * TestSuite constructor.
28 * <pre>
29 * TestSuite suite= new TestSuite(MathTest.class);
30 * </pre>
31 * </p>
32 *
33 * <p>This constructor creates a suite with all the methods
34 * starting with "test" that take no arguments.</p>
35 *
36 * <p>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 * </p>
42 *
43 * @see Test
44 */
45public class TestSuite implements Test {
46
47 /**
48 * ...as the moon sets over the early morning Merlin, Oregon
49 * mountains, our intrepid adventurers type...
50 */
51 static public Test createTest(Class<?> theClass, String name) {
52 Constructor<?> constructor;
53 try {
54 constructor= getTestConstructor(theClass);
55 } catch (NoSuchMethodException e) {
56 return warning("Class "+theClass.getName()+" has no public constructor TestCase(String name) or TestCase()");
57 }
58 Object test;
59 try {
60 if (constructor.getParameterTypes().length == 0) {
61 test= constructor.newInstance(new Object[0]);
62 if (test instanceof TestCase)
63 ((TestCase) test).setName(name);
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(new Class[0]);
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 t) {
106 StringWriter stringWriter= new StringWriter();
107 PrintWriter writer= new PrintWriter(stringWriter);
108 t.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 : superClass.getDeclaredMethods())
150 addTestMethod(each, names, theClass);
151 superClass= superClass.getSuperclass();
152 }
153 if (fTests.size() == 0)
154 addTest(warning("No tests found in "+theClass.getName()));
155 }
156
157 /**
158 * Constructs a TestSuite from the given class with the given name.
159 * @see TestSuite#TestSuite(Class)
160 */
161 public TestSuite(Class<? extends TestCase> theClass, String name) {
162 this(theClass);
163 setName(name);
164 }
165
166 /**
167 * Constructs an empty TestSuite.
168 */
169 public TestSuite(String name) {
170 setName(name);
171 }
172
173 /**
174 * Constructs a TestSuite from the given array of classes.
175 * @param classes {@link TestCase}s
176 */
177 public TestSuite (Class<?>... classes) {
178 for (Class<?> each : classes)
179 addTest(testCaseForClass(each));
180 }
181
182 private Test testCaseForClass(Class<?> each) {
183 if (TestCase.class.isAssignableFrom(each))
184 return new TestSuite(each.asSubclass(TestCase.class));
185 else
186 return warning(each.getCanonicalName() + " does not extend TestCase");
187 }
188
189 /**
190 * Constructs a TestSuite from the given array of classes with the given name.
191 * @see TestSuite#TestSuite(Class[])
192 */
193 public TestSuite(Class<? extends TestCase>[] classes, String name) {
194 this(classes);
195 setName(name);
196 }
197
198 /**
199 * Adds a test to the suite.
200 */
201 public void addTest(Test test) {
202 fTests.add(test);
203 }
204
205 /**
206 * Adds the tests from the given class to the suite
207 */
208 public void addTestSuite(Class<? extends TestCase> testClass) {
209 addTest(new TestSuite(testClass));
210 }
211
212 /**
213 * Counts the number of test cases that will be run by this test.
214 */
215 public int countTestCases() {
216 int count= 0;
217 for (Test each : fTests)
218 count+= each.countTestCases();
219 return count;
220 }
221
222 /**
223 * Returns the name of the suite. Not all
224 * test suites have a name and this method
225 * can return null.
226 */
227 public String getName() {
228 return fName;
229 }
230
231 /**
232 * Runs the tests and collects their result in a TestResult.
233 */
234 public void run(TestResult result) {
235 for (Test each : fTests) {
236 if (result.shouldStop() )
237 break;
238 runTest(each, result);
239 }
240 }
241
242 public void runTest(Test test, TestResult result) {
243 test.run(result);
244 }
245
246 /**
247 * Sets the name of the suite.
248 * @param name the name to set
249 */
250 public void setName(String name) {
251 fName= name;
252 }
253
254 /**
255 * Returns the test at the given index
256 */
257 public Test testAt(int index) {
258 return fTests.get(index);
259 }
260
261 /**
262 * Returns the number of tests in this suite
263 */
264 public int testCount() {
265 return fTests.size();
266 }
267
268 /**
269 * Returns the tests as an enumeration
270 */
271 public Enumeration<Test> tests() {
272 return fTests.elements();
273 }
274
275 /**
276 */
277 @Override
278 public String toString() {
279 if (getName() != null)
280 return getName();
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 if (! isPublicTestMethod(m)) {
289 if (isTestMethod(m))
290 addTest(warning("Test method isn't public: "+ m.getName() + "(" + theClass.getCanonicalName() + ")"));
291 return;
292 }
293 names.add(name);
294 addTest(createTest(theClass, name));
295 }
296
297 private boolean isPublicTestMethod(Method m) {
298 return isTestMethod(m) && Modifier.isPublic(m.getModifiers());
299 }
300
301 private boolean isTestMethod(Method m) {
302 return
303 m.getParameterTypes().length == 0 &&
304 m.getName().startsWith("test") &&
305 m.getReturnType().equals(Void.TYPE);
306 }
307}