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 }