Paul Duffin | cd7c34d | 2016-12-12 16:35:36 +0000 | [diff] [blame] | 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 | */ |
| 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 | */ |
| 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 | */ |
| 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 | } |