001package junit.framework; 002 003import java.lang.reflect.Constructor; 004import java.lang.reflect.InvocationTargetException; 005import java.lang.reflect.Method; 006import java.lang.reflect.Modifier; 007import java.util.ArrayList; 008import java.util.Enumeration; 009import java.util.List; 010import java.util.Vector; 011 012import org.junit.internal.MethodSorter; 013import org.junit.internal.Throwables; 014 015/** 016 * A <code>TestSuite</code> is a <code>Composite</code> of Tests. 017 * It runs a collection of test cases. Here is an example using 018 * the dynamic test definition. 019 * <pre> 020 * TestSuite suite= new TestSuite(); 021 * suite.addTest(new MathTest("testAdd")); 022 * suite.addTest(new MathTest("testDivideByZero")); 023 * </pre> 024 * <p> 025 * 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 * This constructor creates a suite with all the methods 033 * starting with "test" that take no arguments. 034 * <p> 035 * A final option is to do the same for a large array of test classes. 036 * <pre> 037 * Class[] testClasses = { MathTest.class, AnotherTest.class }; 038 * TestSuite suite= new TestSuite(testClasses); 039 * </pre> 040 * 041 * @see Test 042 */ 043public class TestSuite implements Test { 044 045 /** 046 * ...as the moon sets over the early morning Merlin, Oregon 047 * mountains, our intrepid adventurers type... 048 */ 049 static public Test createTest(Class<?> theClass, String name) { 050 Constructor<?> constructor; 051 try { 052 constructor = getTestConstructor(theClass); 053 } catch (NoSuchMethodException e) { 054 return warning("Class " + theClass.getName() + " has no public constructor TestCase(String name) or TestCase()"); 055 } 056 Object test; 057 try { 058 if (constructor.getParameterTypes().length == 0) { 059 test = constructor.newInstance(new Object[0]); 060 if (test instanceof TestCase) { 061 ((TestCase) test).setName(name); 062 } 063 } else { 064 test = constructor.newInstance(new Object[]{name}); 065 } 066 } catch (InstantiationException e) { 067 return (warning("Cannot instantiate test case: " + name + " (" + Throwables.getStacktrace(e) + ")")); 068 } catch (InvocationTargetException e) { 069 return (warning("Exception in constructor: " + name + " (" + Throwables.getStacktrace(e.getTargetException()) + ")")); 070 } catch (IllegalAccessException e) { 071 return (warning("Cannot access test case: " + name + " (" + Throwables.getStacktrace(e) + ")")); 072 } 073 return (Test) test; 074 } 075 076 /** 077 * Gets a constructor which takes a single String as 078 * its argument or a no arg constructor. 079 */ 080 public static Constructor<?> getTestConstructor(Class<?> theClass) throws NoSuchMethodException { 081 try { 082 return theClass.getConstructor(String.class); 083 } catch (NoSuchMethodException e) { 084 // fall through 085 } 086 return theClass.getConstructor(); 087 } 088 089 /** 090 * Returns a test which will fail and log a warning message. 091 */ 092 public static Test warning(final String message) { 093 return new TestCase("warning") { 094 @Override 095 protected void runTest() { 096 fail(message); 097 } 098 }; 099 } 100 101 private String fName; 102 103 private Vector<Test> fTests = new Vector<Test>(10); // Cannot convert this to List because it is used directly by some test runners 104 105 /** 106 * Constructs an empty TestSuite. 107 */ 108 public TestSuite() { 109 } 110 111 /** 112 * Constructs a TestSuite from the given class. Adds all the methods 113 * starting with "test" as test cases to the suite. 114 * Parts of this method were written at 2337 meters in the Hueffihuette, 115 * Kanton Uri 116 */ 117 public TestSuite(final Class<?> theClass) { 118 addTestsFromTestCase(theClass); 119 } 120 121 private void addTestsFromTestCase(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 List<String> names = new ArrayList<String>(); 137 while (Test.class.isAssignableFrom(superClass)) { 138 for (Method each : MethodSorter.getDeclaredMethods(superClass)) { 139 addTestMethod(each, names, theClass); 140 } 141 superClass = superClass.getSuperclass(); 142 } 143 if (fTests.size() == 0) { 144 addTest(warning("No tests found in " + theClass.getName())); 145 } 146 } 147 148 /** 149 * Constructs a TestSuite from the given class with the given name. 150 * 151 * @see TestSuite#TestSuite(Class) 152 */ 153 public TestSuite(Class<? extends TestCase> theClass, String name) { 154 this(theClass); 155 setName(name); 156 } 157 158 /** 159 * Constructs an empty TestSuite. 160 */ 161 public TestSuite(String name) { 162 setName(name); 163 } 164 165 /** 166 * Constructs a TestSuite from the given array of classes. 167 * 168 * @param classes {@link TestCase}s 169 */ 170 public TestSuite(Class<?>... classes) { 171 for (Class<?> each : classes) { 172 addTest(testCaseForClass(each)); 173 } 174 } 175 176 private Test testCaseForClass(Class<?> each) { 177 if (TestCase.class.isAssignableFrom(each)) { 178 return new TestSuite(each.asSubclass(TestCase.class)); 179 } else { 180 return warning(each.getCanonicalName() + " does not extend TestCase"); 181 } 182 } 183 184 /** 185 * Constructs a TestSuite from the given array of classes with the given name. 186 * 187 * @see TestSuite#TestSuite(Class[]) 188 */ 189 public TestSuite(Class<? extends TestCase>[] classes, String name) { 190 this(classes); 191 setName(name); 192 } 193 194 /** 195 * Adds a test to the suite. 196 */ 197 public void addTest(Test test) { 198 fTests.add(test); 199 } 200 201 /** 202 * Adds the tests from the given class to the suite. 203 */ 204 public void addTestSuite(Class<? extends TestCase> testClass) { 205 addTest(new TestSuite(testClass)); 206 } 207 208 /** 209 * Counts the number of test cases that will be run by this test. 210 */ 211 public int countTestCases() { 212 int count = 0; 213 for (Test each : fTests) { 214 count += each.countTestCases(); 215 } 216 return count; 217 } 218 219 /** 220 * Returns the name of the suite. Not all 221 * test suites have a name and this method 222 * can return null. 223 */ 224 public String getName() { 225 return fName; 226 } 227 228 /** 229 * Runs the tests and collects their result in a TestResult. 230 */ 231 public void run(TestResult result) { 232 for (Test each : fTests) { 233 if (result.shouldStop()) { 234 break; 235 } 236 runTest(each, result); 237 } 238 } 239 240 public void runTest(Test test, TestResult result) { 241 test.run(result); 242 } 243 244 /** 245 * Sets the name of the suite. 246 * 247 * @param name the name to set 248 */ 249 public void setName(String name) { 250 fName = name; 251 } 252 253 /** 254 * Returns the test at the given index. 255 */ 256 public Test testAt(int index) { 257 return fTests.get(index); 258 } 259 260 /** 261 * Returns the number of tests in this suite. 262 */ 263 public int testCount() { 264 return fTests.size(); 265 } 266 267 /** 268 * Returns the tests as an enumeration. 269 */ 270 public Enumeration<Test> tests() { 271 return fTests.elements(); 272 } 273 274 /** 275 */ 276 @Override 277 public String toString() { 278 if (getName() != null) { 279 return getName(); 280 } 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 } 289 if (!isPublicTestMethod(m)) { 290 if (isTestMethod(m)) { 291 addTest(warning("Test method isn't public: " + m.getName() + "(" + theClass.getCanonicalName() + ")")); 292 } 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}