1. package junit.framework;
  2. import java.util.Vector;
  3. import java.util.Enumeration;
  4. import java.io.PrintWriter;
  5. import java.io.StringWriter;
  6. import java.lang.reflect.*;
  7. import java.lang.reflect.Constructor;
  8. /**
  9. * A <code>TestSuite</code> is a <code>Composite</code> of Tests.
  10. * It runs a collection of test cases. Here is an example using
  11. * the dynamic test definition.
  12. * <pre>
  13. * TestSuite suite= new TestSuite();
  14. * suite.addTest(new MathTest("testAdd"));
  15. * suite.addTest(new MathTest("testDivideByZero"));
  16. * </pre>
  17. * Alternatively, a TestSuite can extract the tests to be run automatically.
  18. * To do so you pass the class of your TestCase class to the
  19. * TestSuite constructor.
  20. * <pre>
  21. * TestSuite suite= new TestSuite(MathTest.class);
  22. * </pre>
  23. * This constructor creates a suite with all the methods
  24. * starting with "test" that take no arguments.
  25. *
  26. * @see Test
  27. */
  28. public class TestSuite implements Test {
  29. private Vector fTests= new Vector(10);
  30. private String fName;
  31. /**
  32. * Constructs an empty TestSuite.
  33. */
  34. public TestSuite() {
  35. }
  36. /**
  37. * Constructs a TestSuite from the given class with the given name.
  38. * @see TestSuite#TestSuite(Class)
  39. */
  40. public TestSuite(Class theClass, String name) {
  41. this(theClass);
  42. setName(name);
  43. }
  44. /**
  45. * Constructs a TestSuite from the given class. Adds all the methods
  46. * starting with "test" as test cases to the suite.
  47. * Parts of this method was written at 2337 meters in the Hüffihütte,
  48. * Kanton Uri
  49. */
  50. public TestSuite(final Class theClass) {
  51. fName= theClass.getName();
  52. try {
  53. getTestConstructor(theClass); // Avoid generating multiple error messages
  54. } catch (NoSuchMethodException e) {
  55. addTest(warning("Class "+theClass.getName()+" has no public constructor TestCase(String name) or TestCase()"));
  56. return;
  57. }
  58. if (!Modifier.isPublic(theClass.getModifiers())) {
  59. addTest(warning("Class "+theClass.getName()+" is not public"));
  60. return;
  61. }
  62. Class superClass= theClass;
  63. Vector names= new Vector();
  64. while (Test.class.isAssignableFrom(superClass)) {
  65. Method[] methods= superClass.getDeclaredMethods();
  66. for (int i= 0; i < methods.length; i++) {
  67. addTestMethod(methods[i], names, theClass);
  68. }
  69. superClass= superClass.getSuperclass();
  70. }
  71. if (fTests.size() == 0)
  72. addTest(warning("No tests found in "+theClass.getName()));
  73. }
  74. /**
  75. * Constructs an empty TestSuite.
  76. */
  77. public TestSuite(String name) {
  78. setName(name);
  79. }
  80. /**
  81. * Adds a test to the suite.
  82. */
  83. public void addTest(Test test) {
  84. fTests.addElement(test);
  85. }
  86. /**
  87. * Adds the tests from the given class to the suite
  88. */
  89. public void addTestSuite(Class testClass) {
  90. addTest(new TestSuite(testClass));
  91. }
  92. private void addTestMethod(Method m, Vector names, Class theClass) {
  93. String name= m.getName();
  94. if (names.contains(name))
  95. return;
  96. if (! isPublicTestMethod(m)) {
  97. if (isTestMethod(m))
  98. addTest(warning("Test method isn't public: "+m.getName()));
  99. return;
  100. }
  101. names.addElement(name);
  102. addTest(createTest(theClass, name));
  103. }
  104. /**
  105. * ...as the moon sets over the early morning Merlin, Oregon
  106. * mountains, our intrepid adventurers type...
  107. */
  108. static public Test createTest(Class theClass, String name) {
  109. Constructor constructor;
  110. try {
  111. constructor= getTestConstructor(theClass);
  112. } catch (NoSuchMethodException e) {
  113. return warning("Class "+theClass.getName()+" has no public constructor TestCase(String name) or TestCase()");
  114. }
  115. Object test;
  116. try {
  117. if (constructor.getParameterTypes().length == 0) {
  118. test= constructor.newInstance(new Object[0]);
  119. if (test instanceof TestCase)
  120. ((TestCase) test).setName(name);
  121. } else {
  122. test= constructor.newInstance(new Object[]{name});
  123. }
  124. } catch (InstantiationException e) {
  125. return(warning("Cannot instantiate test case: "+name+" ("+exceptionToString(e)+")"));
  126. } catch (InvocationTargetException e) {
  127. return(warning("Exception in constructor: "+name+" ("+exceptionToString(e.getTargetException())+")"));
  128. } catch (IllegalAccessException e) {
  129. return(warning("Cannot access test case: "+name+" ("+exceptionToString(e)+")"));
  130. }
  131. return (Test) test;
  132. }
  133. /**
  134. * Converts the stack trace into a string
  135. */
  136. private static String exceptionToString(Throwable t) {
  137. StringWriter stringWriter= new StringWriter();
  138. PrintWriter writer= new PrintWriter(stringWriter);
  139. t.printStackTrace(writer);
  140. return stringWriter.toString();
  141. }
  142. /**
  143. * Counts the number of test cases that will be run by this test.
  144. */
  145. public int countTestCases() {
  146. int count= 0;
  147. for (Enumeration e= tests(); e.hasMoreElements(); ) {
  148. Test test= (Test)e.nextElement();
  149. count= count + test.countTestCases();
  150. }
  151. return count;
  152. }
  153. /**
  154. * Gets a constructor which takes a single String as
  155. * its argument or a no arg constructor.
  156. */
  157. public static Constructor getTestConstructor(Class theClass) throws NoSuchMethodException {
  158. Class[] args= { String.class };
  159. try {
  160. return theClass.getConstructor(args);
  161. } catch (NoSuchMethodException e) {
  162. // fall through
  163. }
  164. return theClass.getConstructor(new Class[0]);
  165. }
  166. private boolean isPublicTestMethod(Method m) {
  167. return isTestMethod(m) && Modifier.isPublic(m.getModifiers());
  168. }
  169. private boolean isTestMethod(Method m) {
  170. String name= m.getName();
  171. Class[] parameters= m.getParameterTypes();
  172. Class returnType= m.getReturnType();
  173. return parameters.length == 0 && name.startsWith("test") && returnType.equals(Void.TYPE);
  174. }
  175. /**
  176. * Runs the tests and collects their result in a TestResult.
  177. */
  178. public void run(TestResult result) {
  179. for (Enumeration e= tests(); e.hasMoreElements(); ) {
  180. if (result.shouldStop() )
  181. break;
  182. Test test= (Test)e.nextElement();
  183. runTest(test, result);
  184. }
  185. }
  186. public void runTest(Test test, TestResult result) {
  187. test.run(result);
  188. }
  189. /**
  190. * Returns the test at the given index
  191. */
  192. public Test testAt(int index) {
  193. return (Test)fTests.elementAt(index);
  194. }
  195. /**
  196. * Returns the number of tests in this suite
  197. */
  198. public int testCount() {
  199. return fTests.size();
  200. }
  201. /**
  202. * Returns the tests as an enumeration
  203. */
  204. public Enumeration tests() {
  205. return fTests.elements();
  206. }
  207. /**
  208. */
  209. public String toString() {
  210. if (getName() != null)
  211. return getName();
  212. return super.toString();
  213. }
  214. /**
  215. * Sets the name of the suite.
  216. * @param name The name to set
  217. */
  218. public void setName(String name) {
  219. fName= name;
  220. }
  221. /**
  222. * Returns the name of the suite. Not all
  223. * test suites have a name and this method
  224. * can return null.
  225. */
  226. public String getName() {
  227. return fName;
  228. }
  229. /**
  230. * Returns a test which will fail and log a warning message.
  231. */
  232. private static Test warning(final String message) {
  233. return new TestCase("warning") {
  234. protected void runTest() {
  235. fail(message);
  236. }
  237. };
  238. }
  239. }