Open Source Repository

Home /junit/junit-4.10 | Repository Home



junit/framework/TestSuite.java
package junit.framework;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Vector;

/**
 <p><code>TestSuite</code> is a <code>Composite</code> of Tests.
 * It runs a collection of test cases. Here is an example using
 * the dynamic test definition.
 <pre>
 * TestSuite suite= new TestSuite();
 * suite.addTest(new MathTest("testAdd"));
 * suite.addTest(new MathTest("testDivideByZero"));
 </pre>
 </p>
 
 <p>Alternatively, a TestSuite can extract the tests to be run automatically.
 * To do so you pass the class of your TestCase class to the
 * TestSuite constructor.
 <pre>
 * TestSuite suite= new TestSuite(MathTest.class);
 </pre>
 </p>
 
 <p>This constructor creates a suite with all the methods
 * starting with "test" that take no arguments.</p>
 
 <p>A final option is to do the same for a large array of test classes.
 <pre>
 * Class[] testClasses = { MathTest.class, AnotherTest.class }
 * TestSuite suite= new TestSuite(testClasses);
 </pre>
 </p>
 *
 @see Test
 */
public class TestSuite implements Test {

  /**
   * ...as the moon sets over the early morning Merlin, Oregon
   * mountains, our intrepid adventurers type...
   */
  static public Test createTest(Class<?> theClass, String name) {
    Constructor<?> constructor;
    try {
      constructor= getTestConstructor(theClass);
    catch (NoSuchMethodException e) {
      return warning("Class "+theClass.getName()+" has no public constructor TestCase(String name) or TestCase()");
    }
    Object test;
    try {
      if (constructor.getParameterTypes().length == 0) {
        test= constructor.newInstance(new Object[0]);
        if (test instanceof TestCase)
          ((TestCasetest).setName(name);
      else {
        test= constructor.newInstance(new Object[]{name});
      }
    catch (InstantiationException e) {
      return(warning("Cannot instantiate test case: "+name+" ("+exceptionToString(e)+")"));
    catch (InvocationTargetException e) {
      return(warning("Exception in constructor: "+name+" ("+exceptionToString(e.getTargetException())+")"));
    catch (IllegalAccessException e) {
      return(warning("Cannot access test case: "+name+" ("+exceptionToString(e)+")"));
    }
    return (Testtest;
  }
  
  /**
   * Gets a constructor which takes a single String as
   * its argument or a no arg constructor.
   */
  public static Constructor<?> getTestConstructor(Class<?> theClassthrows NoSuchMethodException {
    try {
      return theClass.getConstructor(String.class);  
    catch (NoSuchMethodException e) {
      // fall through
    }
    return theClass.getConstructor(new Class[0]);
  }

  /**
   * Returns a test which will fail and log a warning message.
   */
  public static Test warning(final String message) {
    return new TestCase("warning") {
      @Override
      protected void runTest() {
        fail(message);
      }
    };
  }

  /**
   * Converts the stack trace into a string
   */
  private static String exceptionToString(Throwable t) {
    StringWriter stringWriter= new StringWriter();
    PrintWriter writer= new PrintWriter(stringWriter);
    t.printStackTrace(writer);
    return stringWriter.toString();
  }
  
  private String fName;

  private Vector<Test> fTests= new Vector<Test>(10)// Cannot convert this to List because it is used directly by some test runners

    /**
   * Constructs an empty TestSuite.
   */
  public TestSuite() {
  }
  
  /**
   * Constructs a TestSuite from the given class. Adds all the methods
   * starting with "test" as test cases to the suite.
   * Parts of this method were written at 2337 meters in the Hueffihuette,
   * Kanton Uri
   */
  public TestSuite(final Class<?> theClass) {
    addTestsFromTestCase(theClass);
  }

  private void addTestsFromTestCase(final Class<?> theClass) {
    fName= theClass.getName();
    try {
      getTestConstructor(theClass)// Avoid generating multiple error messages
    catch (NoSuchMethodException e) {
      addTest(warning("Class "+theClass.getName()+" has no public constructor TestCase(String name) or TestCase()"));
      return;
    }

    if (!Modifier.isPublic(theClass.getModifiers())) {
      addTest(warning("Class "+theClass.getName()+" is not public"));
      return;
    }

    Class<?> superClass= theClass;
    List<String> names= new ArrayList<String>();
    while (Test.class.isAssignableFrom(superClass)) {
      for (Method each : superClass.getDeclaredMethods())
        addTestMethod(each, names, theClass);
      superClass= superClass.getSuperclass();
    }
    if (fTests.size() == 0)
      addTest(warning("No tests found in "+theClass.getName()));
  }
  
  /**
   * Constructs a TestSuite from the given class with the given name.
   @see TestSuite#TestSuite(Class)
   */
  public TestSuite(Class<? extends TestCase>  theClass, String name) {
    this(theClass);
    setName(name);
  }
  
     /**
   * Constructs an empty TestSuite.
   */
  public TestSuite(String name) {
    setName(name);
  }
  
  /**
   * Constructs a TestSuite from the given array of classes.  
   @param classes {@link TestCase}s
   */
  public TestSuite (Class<?>... classes) {
    for (Class<?> each : classes)
      addTest(testCaseForClass(each));
  }

  private Test testCaseForClass(Class<?> each) {
    if (TestCase.class.isAssignableFrom(each))
      return new TestSuite(each.asSubclass(TestCase.class));
    else
      return warning(each.getCanonicalName() " does not extend TestCase");
  }
  
  /**
   * Constructs a TestSuite from the given array of classes with the given name.
   @see TestSuite#TestSuite(Class[])
   */
  public TestSuite(Class<? extends TestCase>[] classes, String name) {
    this(classes);
    setName(name);
  }
  
  /**
   * Adds a test to the suite.
   */
  public void addTest(Test test) {
    fTests.add(test);
  }

  /**
   * Adds the tests from the given class to the suite
   */
  public void addTestSuite(Class<? extends TestCase> testClass) {
    addTest(new TestSuite(testClass));
  }
  
  /**
   * Counts the number of test cases that will be run by this test.
   */
  public int countTestCases() {
    int count= 0;
    for (Test each : fTests)
      count+=  each.countTestCases();
    return count;
  }

  /**
   * Returns the name of the suite. Not all
   * test suites have a name and this method
   * can return null.
   */
  public String getName() {
    return fName;
  }
   
  /**
   * Runs the tests and collects their result in a TestResult.
   */
  public void run(TestResult result) {
    for (Test each : fTests) {
        if (result.shouldStop() )
          break;
      runTest(each, result);
    }
  }

  public void runTest(Test test, TestResult result) {
    test.run(result);
  }
   
  /**
   * Sets the name of the suite.
   @param name the name to set
   */
  public void setName(String name) {
    fName= name;
  }

  /**
   * Returns the test at the given index
   */
  public Test testAt(int index) {
    return fTests.get(index);
  }
  
  /**
   * Returns the number of tests in this suite
   */
  public int testCount() {
    return fTests.size();
  }
  
  /**
   * Returns the tests as an enumeration
   */
  public Enumeration<Test> tests() {
    return fTests.elements();
  }
  
  /**
   */
  @Override
  public String toString() {
    if (getName() != null)
      return getName();
    return super.toString();
   }

  private void addTestMethod(Method m, List<String> names, Class<?> theClass) {
    String name= m.getName();
    if (names.contains(name))
      return;
    if (! isPublicTestMethod(m)) {
      if (isTestMethod(m))
        addTest(warning("Test method isn't public: "+ m.getName() "(" + theClass.getCanonicalName() ")"));
      return;
    }
    names.add(name);
    addTest(createTest(theClass, name));
  }

  private boolean isPublicTestMethod(Method m) {
    return isTestMethod(m&& Modifier.isPublic(m.getModifiers());
   }
   
  private boolean isTestMethod(Method m) {
    return 
      m.getParameterTypes().length == && 
      m.getName().startsWith("test"&& 
      m.getReturnType().equals(Void.TYPE);
   }
}