Home | History | Annotate | Download | only in framework
      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 }