1 package junit.framework;
2
3 import java.util.Vector;
4 import java.util.Enumeration;
5 import java.io.PrintWriter;
6 import java.io.StringWriter;
7 import java.lang.reflect.*;
8 import java.lang.reflect.Constructor;
9
10 /***
11 * A <code>TestSuite</code> is a <code>Composite</code> of Tests.
12 * It runs a collection of test cases. Here is an example using
13 * the dynamic test definition.
14 * <pre>
15 * TestSuite suite= new TestSuite();
16 * suite.addTest(new MathTest("testAdd"));
17 * suite.addTest(new MathTest("testDivideByZero"));
18 * </pre>
19 * Alternatively, a TestSuite can extract the tests to be run automatically.
20 * To do so you pass the class of your TestCase class to the
21 * TestSuite constructor.
22 * <pre>
23 * TestSuite suite= new TestSuite(MathTest.class);
24 * </pre>
25 * This constructor creates a suite with all the methods
26 * starting with "test" that take no arguments.
27 *
28 * @see Test
29 */
30 public class TestSuite implements Test {
31
32 private Vector fTests= new Vector(10);
33 private String fName;
34
35 /***
36 * Constructs an empty TestSuite.
37 */
38 public TestSuite() {
39 }
40
41 /***
42 * Constructs a TestSuite from the given class with the given name.
43 * @see TestSuite#TestSuite(Class)
44 */
45 public TestSuite(Class theClass, String name) {
46 this(theClass);
47 setName(name);
48 }
49
50 /***
51 * Constructs a TestSuite from the given class. Adds all the methods
52 * starting with "test" as test cases to the suite.
53 * Parts of this method was written at 2337 meters in the H?ffih?tte,
54 * Kanton Uri
55 */
56 public TestSuite(final Class theClass) {
57 fName= theClass.getName();
58 try {
59 getTestConstructor(theClass);
60 } catch (NoSuchMethodException e) {
61 addTest(warning("Class "+theClass.getName()+" has no public constructor TestCase(String name) or TestCase()"));
62 return;
63 }
64
65 if (!Modifier.isPublic(theClass.getModifiers())) {
66 addTest(warning("Class "+theClass.getName()+" is not public"));
67 return;
68 }
69
70 Class superClass= theClass;
71 Vector names= new Vector();
72 while (Test.class.isAssignableFrom(superClass)) {
73 Method[] methods= superClass.getDeclaredMethods();
74 for (int i= 0; i < methods.length; i++) {
75 addTestMethod(methods[i], names, theClass);
76 }
77 superClass= superClass.getSuperclass();
78 }
79 if (fTests.size() == 0)
80 addTest(warning("No tests found in "+theClass.getName()));
81 }
82
83 /***
84 * Constructs an empty TestSuite.
85 */
86 public TestSuite(String name) {
87 setName(name);
88 }
89
90 /***
91 * Adds a test to the suite.
92 */
93 public void addTest(Test test) {
94 fTests.addElement(test);
95 }
96
97 /***
98 * Adds the tests from the given class to the suite
99 */
100 public void addTestSuite(Class testClass) {
101 addTest(new TestSuite(testClass));
102 }
103
104 private void addTestMethod(Method m, Vector names, Class theClass) {
105 String name= m.getName();
106 if (names.contains(name))
107 return;
108 if (! isPublicTestMethod(m)) {
109 if (isTestMethod(m))
110 addTest(warning("Test method isn't public: "+m.getName()));
111 return;
112 }
113 names.addElement(name);
114 addTest(createTest(theClass, name));
115 }
116
117 /***
118 * ...as the moon sets over the early morning Merlin, Oregon
119 * mountains, our intrepid adventurers type...
120 */
121 static public Test createTest(Class theClass, String name) {
122 Constructor constructor;
123 try {
124 constructor= getTestConstructor(theClass);
125 } catch (NoSuchMethodException e) {
126 return warning("Class "+theClass.getName()+" has no public constructor TestCase(String name) or TestCase()");
127 }
128 Object test;
129 try {
130 if (constructor.getParameterTypes().length == 0) {
131 test= constructor.newInstance(new Object[0]);
132 if (test instanceof TestCase)
133 ((TestCase) test).setName(name);
134 } else {
135 test= constructor.newInstance(new Object[]{name});
136 }
137 } catch (InstantiationException e) {
138 return(warning("Cannot instantiate test case: "+name+" ("+exceptionToString(e)+")"));
139 } catch (InvocationTargetException e) {
140 return(warning("Exception in constructor: "+name+" ("+exceptionToString(e.getTargetException())+")"));
141 } catch (IllegalAccessException e) {
142 return(warning("Cannot access test case: "+name+" ("+exceptionToString(e)+")"));
143 }
144 return (Test) test;
145 }
146
147 /***
148 * Converts the stack trace into a string
149 */
150 private static String exceptionToString(Throwable t) {
151 StringWriter stringWriter= new StringWriter();
152 PrintWriter writer= new PrintWriter(stringWriter);
153 t.printStackTrace(writer);
154 return stringWriter.toString();
155
156 }
157
158 /***
159 * Counts the number of test cases that will be run by this test.
160 */
161 public int countTestCases() {
162 int count= 0;
163 for (Enumeration e= tests(); e.hasMoreElements(); ) {
164 Test test= (Test)e.nextElement();
165 count= count + test.countTestCases();
166 }
167 return count;
168 }
169
170 /***
171 * Gets a constructor which takes a single String as
172 * its argument or a no arg constructor.
173 */
174 public static Constructor getTestConstructor(Class theClass) throws NoSuchMethodException {
175 Class[] args= { String.class };
176 try {
177 return theClass.getConstructor(args);
178 } catch (NoSuchMethodException e) {
179
180 }
181 return theClass.getConstructor(new Class[0]);
182 }
183
184 private boolean isPublicTestMethod(Method m) {
185 return isTestMethod(m) && Modifier.isPublic(m.getModifiers());
186 }
187
188 private boolean isTestMethod(Method m) {
189 String name= m.getName();
190 Class[] parameters= m.getParameterTypes();
191 Class returnType= m.getReturnType();
192 return parameters.length == 0 && name.startsWith("test") && returnType.equals(Void.TYPE);
193 }
194
195 /***
196 * Runs the tests and collects their result in a TestResult.
197 */
198 public void run(TestResult result) {
199 for (Enumeration e= tests(); e.hasMoreElements(); ) {
200 if (result.shouldStop() )
201 break;
202 Test test= (Test)e.nextElement();
203 runTest(test, result);
204 }
205 }
206
207 public void runTest(Test test, TestResult result) {
208 test.run(result);
209 }
210
211 /***
212 * Returns the test at the given index
213 */
214 public Test testAt(int index) {
215 return (Test)fTests.elementAt(index);
216 }
217
218 /***
219 * Returns the number of tests in this suite
220 */
221 public int testCount() {
222 return fTests.size();
223 }
224
225 /***
226 * Returns the tests as an enumeration
227 */
228 public Enumeration tests() {
229 return fTests.elements();
230 }
231
232 /***
233 */
234 public String toString() {
235 if (getName() != null)
236 return getName();
237 return super.toString();
238 }
239
240 /***
241 * Sets the name of the suite.
242 * @param name The name to set
243 */
244 public void setName(String name) {
245 fName= name;
246 }
247
248 /***
249 * Returns the name of the suite. Not all
250 * test suites have a name and this method
251 * can return null.
252 */
253 public String getName() {
254 return fName;
255 }
256
257 /***
258 * Returns a test which will fail and log a warning message.
259 */
260 private static Test warning(final String message) {
261 return new TestCase("warning") {
262 protected void runTest() {
263 fail(message);
264 }
265 };
266 }
267 }