001: package junit.framework;
002:
003: import java.io.PrintWriter;
004: import java.io.StringWriter;
005: import java.lang.reflect.Constructor;
006: import java.lang.reflect.InvocationTargetException;
007: import java.lang.reflect.Method;
008: import java.lang.reflect.Modifier;
009: import java.util.ArrayList;
010: import java.util.Enumeration;
011: import java.util.List;
012: import java.util.Vector;
013:
014: /**
015: * <p>A <code>TestSuite</code> is a <code>Composite</code> of Tests.
016: * It runs a collection of test cases. Here is an example using
017: * the dynamic test definition.
018: * <pre>
019: * TestSuite suite= new TestSuite();
020: * suite.addTest(new MathTest("testAdd"));
021: * suite.addTest(new MathTest("testDivideByZero"));
022: * </pre>
023: * </p>
024: *
025: * <p>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: *
033: * <p>This constructor creates a suite with all the methods
034: * starting with "test" that take no arguments.</p>
035: *
036: * <p>A final option is to do the same for a large array of test classes.
037: * <pre>
038: * Class[] testClasses = { MathTest.class, AnotherTest.class }
039: * TestSuite suite= new TestSuite(testClasses);
040: * </pre>
041: * </p>
042: *
043: * @see Test
044: */
045: public class TestSuite implements Test {
046:
047: /**
048: * ...as the moon sets over the early morning Merlin, Oregon
049: * mountains, our intrepid adventurers type...
050: */
051: static public Test createTest(Class<? extends TestCase> theClass,
052: String name) {
053: Constructor<? extends TestCase> constructor;
054: try {
055: constructor = getTestConstructor(theClass);
056: } catch (NoSuchMethodException e) {
057: return warning("Class "
058: + theClass.getName()
059: + " has no public constructor TestCase(String name) or TestCase()");
060: }
061: Object test;
062: try {
063: if (constructor.getParameterTypes().length == 0) {
064: test = constructor.newInstance(new Object[0]);
065: if (test instanceof TestCase)
066: ((TestCase) test).setName(name);
067: } else {
068: test = constructor.newInstance(new Object[] { name });
069: }
070: } catch (InstantiationException e) {
071: return (warning("Cannot instantiate test case: " + name
072: + " (" + exceptionToString(e) + ")"));
073: } catch (InvocationTargetException e) {
074: return (warning("Exception in constructor: " + name + " ("
075: + exceptionToString(e.getTargetException()) + ")"));
076: } catch (IllegalAccessException e) {
077: return (warning("Cannot access test case: " + name + " ("
078: + exceptionToString(e) + ")"));
079: }
080: return (Test) test;
081: }
082:
083: /**
084: * Gets a constructor which takes a single String as
085: * its argument or a no arg constructor.
086: */
087: public static Constructor<? extends TestCase> getTestConstructor(
088: Class<? extends TestCase> theClass)
089: throws NoSuchMethodException {
090: try {
091: return theClass.getConstructor(String.class);
092: } catch (NoSuchMethodException e) {
093: // fall through
094: }
095: return theClass.getConstructor(new Class[0]);
096: }
097:
098: /**
099: * Returns a test which will fail and log a warning message.
100: */
101: public static Test warning(final String message) {
102: return new TestCase("warning") {
103: @Override
104: protected void runTest() {
105: fail(message);
106: }
107: };
108: }
109:
110: /**
111: * Converts the stack trace into a string
112: */
113: private static String exceptionToString(Throwable t) {
114: StringWriter stringWriter = new StringWriter();
115: PrintWriter writer = new PrintWriter(stringWriter);
116: t.printStackTrace(writer);
117: return stringWriter.toString();
118: }
119:
120: private String fName;
121:
122: private Vector<Test> fTests = new Vector<Test>(10); // Cannot convert this to List because it is used directly by some test runners
123:
124: /**
125: * Constructs an empty TestSuite.
126: */
127: public TestSuite() {
128: }
129:
130: /**
131: * Constructs a TestSuite from the given class. Adds all the methods
132: * starting with "test" as test cases to the suite.
133: * Parts of this method were written at 2337 meters in the Hueffihuette,
134: * Kanton Uri
135: */
136: public TestSuite(final Class<? extends TestCase> theClass) {
137: fName = theClass.getName();
138: try {
139: getTestConstructor(theClass); // Avoid generating multiple error messages
140: } catch (NoSuchMethodException e) {
141: addTest(warning("Class "
142: + theClass.getName()
143: + " has no public constructor TestCase(String name) or TestCase()"));
144: return;
145: }
146:
147: if (!Modifier.isPublic(theClass.getModifiers())) {
148: addTest(warning("Class " + theClass.getName()
149: + " is not public"));
150: return;
151: }
152:
153: Class<?> super Class = theClass;
154: List<String> names = new ArrayList<String>();
155: while (Test.class.isAssignableFrom(super Class)) {
156: for (Method each : super Class.getDeclaredMethods())
157: addTestMethod(each, names, theClass);
158: super Class = super Class.getSuperclass();
159: }
160: if (fTests.size() == 0)
161: addTest(warning("No tests found in " + theClass.getName()));
162: }
163:
164: /**
165: * Constructs a TestSuite from the given class with the given name.
166: * @see TestSuite#TestSuite(Class)
167: */
168: public TestSuite(Class<? extends TestCase> theClass, String name) {
169: this (theClass);
170: setName(name);
171: }
172:
173: /**
174: * Constructs an empty TestSuite.
175: */
176: public TestSuite(String name) {
177: setName(name);
178: }
179:
180: /**
181: * Constructs a TestSuite from the given array of classes.
182: * @param classes {@link TestCase}s
183: */
184: public TestSuite(Class<?>... classes) {
185: for (Class<?> each : classes)
186: addTest(new TestSuite(each.asSubclass(TestCase.class)));
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,
285: Class<? extends TestCase> theClass) {
286: String name = m.getName();
287: if (names.contains(name))
288: return;
289: if (!isPublicTestMethod(m)) {
290: if (isTestMethod(m))
291: addTest(warning("Test method isn't public: "
292: + m.getName()));
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: }
|