View Javadoc

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); // Avoid generating multiple error messages
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 			// fall through
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 }