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 import org.junit.internal.MethodSorter; 15 16 /** 17 * A <code>TestSuite</code> is a <code>Composite</code> of Tests. 18 * It runs a collection of test cases. Here is an example using 19 * the dynamic test definition. 20 * <pre> 21 * TestSuite suite= new TestSuite(); 22 * suite.addTest(new MathTest("testAdd")); 23 * suite.addTest(new MathTest("testDivideByZero")); 24 * </pre> 25 * <p> 26 * Alternatively, a TestSuite can extract the tests to be run automatically. 27 * To do so you pass the class of your TestCase class to the 28 * TestSuite constructor. 29 * <pre> 30 * TestSuite suite= new TestSuite(MathTest.class); 31 * </pre> 32 * <p> 33 * This constructor creates a suite with all the methods 34 * starting with "test" that take no arguments. 35 * <p> 36 * 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 * 42 * @see Test 43 */ 44 public class TestSuite implements Test { 45 46 /** 47 * ...as the moon sets over the early morning Merlin, Oregon 48 * mountains, our intrepid adventurers type... 49 */ createTest(Class<?> theClass, String name)50 static public Test createTest(Class<?> theClass, String name) { 51 Constructor<?> constructor; 52 try { 53 constructor = getTestConstructor(theClass); 54 } catch (NoSuchMethodException e) { 55 return warning("Class " + theClass.getName() + " has no public constructor TestCase(String name) or TestCase()"); 56 } 57 Object test; 58 try { 59 if (constructor.getParameterTypes().length == 0) { 60 test = constructor.newInstance(new Object[0]); 61 if (test instanceof TestCase) { 62 ((TestCase) test).setName(name); 63 } 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(); 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 e) { 106 StringWriter stringWriter = new StringWriter(); 107 PrintWriter writer = new PrintWriter(stringWriter); 108 e.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 : MethodSorter.getDeclaredMethods(superClass)) { 150 addTestMethod(each, names, theClass); 151 } 152 superClass = superClass.getSuperclass(); 153 } 154 if (fTests.size() == 0) { 155 addTest(warning("No tests found in " + theClass.getName())); 156 } 157 } 158 159 /** 160 * Constructs a TestSuite from the given class with the given name. 161 * 162 * @see TestSuite#TestSuite(Class) 163 */ 164 public TestSuite(Class<? extends TestCase> theClass, String name) { 165 this(theClass); 166 setName(name); 167 } 168 169 /** 170 * Constructs an empty TestSuite. 171 */ 172 public TestSuite(String name) { 173 setName(name); 174 } 175 176 /** 177 * Constructs a TestSuite from the given array of classes. 178 * 179 * @param classes {@link TestCase}s 180 */ 181 public TestSuite(Class<?>... classes) { 182 for (Class<?> each : classes) { 183 addTest(testCaseForClass(each)); 184 } 185 } 186 187 private Test testCaseForClass(Class<?> each) { 188 if (TestCase.class.isAssignableFrom(each)) { 189 return new TestSuite(each.asSubclass(TestCase.class)); 190 } else { 191 return warning(each.getCanonicalName() + " does not extend TestCase"); 192 } 193 } 194 195 /** 196 * Constructs a TestSuite from the given array of classes with the given name. 197 * 198 * @see TestSuite#TestSuite(Class[]) 199 */ 200 public TestSuite(Class<? extends TestCase>[] classes, String name) { 201 this(classes); 202 setName(name); 203 } 204 205 /** 206 * Adds a test to the suite. 207 */ 208 public void addTest(Test test) { 209 fTests.add(test); 210 } 211 212 /** 213 * Adds the tests from the given class to the suite 214 */ 215 public void addTestSuite(Class<? extends TestCase> testClass) { 216 addTest(new TestSuite(testClass)); 217 } 218 219 /** 220 * Counts the number of test cases that will be run by this test. 221 */ 222 public int countTestCases() { 223 int count = 0; 224 for (Test each : fTests) { 225 count += each.countTestCases(); 226 } 227 return count; 228 } 229 230 /** 231 * Returns the name of the suite. Not all 232 * test suites have a name and this method 233 * can return null. 234 */ 235 public String getName() { 236 return fName; 237 } 238 239 /** 240 * Runs the tests and collects their result in a TestResult. 241 */ 242 public void run(TestResult result) { 243 for (Test each : fTests) { 244 if (result.shouldStop()) { 245 break; 246 } 247 runTest(each, result); 248 } 249 } 250 251 public void runTest(Test test, TestResult result) { 252 test.run(result); 253 } 254 255 /** 256 * Sets the name of the suite. 257 * 258 * @param name the name to set 259 */ 260 public void setName(String name) { 261 fName = name; 262 } 263 264 /** 265 * Returns the test at the given index 266 */ 267 public Test testAt(int index) { 268 return fTests.get(index); 269 } 270 271 /** 272 * Returns the number of tests in this suite 273 */ 274 public int testCount() { 275 return fTests.size(); 276 } 277 278 /** 279 * Returns the tests as an enumeration 280 */ 281 public Enumeration<Test> tests() { 282 return fTests.elements(); 283 } 284 285 /** 286 */ 287 @Override 288 public String toString() { 289 if (getName() != null) { 290 return getName(); 291 } 292 return super.toString(); 293 } 294 295 private void addTestMethod(Method m, List<String> names, Class<?> theClass) { 296 String name = m.getName(); 297 if (names.contains(name)) { 298 return; 299 } 300 if (!isPublicTestMethod(m)) { 301 if (isTestMethod(m)) { 302 addTest(warning("Test method isn't public: " + m.getName() + "(" + theClass.getCanonicalName() + ")")); 303 } 304 return; 305 } 306 names.add(name); 307 addTest(createTest(theClass, name)); 308 } 309 310 private boolean isPublicTestMethod(Method m) { 311 return isTestMethod(m) && Modifier.isPublic(m.getModifiers()); 312 } 313 314 private boolean isTestMethod(Method m) { 315 return m.getParameterTypes().length == 0 && 316 m.getName().startsWith("test") && 317 m.getReturnType().equals(Void.TYPE); 318 } 319 } 320