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 /**
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  */
45 public class TestSuite implements Test {
46 
47 	/**
48 	 * ...as the moon sets over the early morning Merlin, Oregon
49 	 * mountains, our intrepid adventurers type...
50 	 */
createTest(Class<?> theClass, String name)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 	 */
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(new Class[0]);
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 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 }