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