Open Source Repository

Home /junit/junit-4.10 | Repository Home



org/junit/runners/model/RunnerBuilder.java
package org.junit.runners.model;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.junit.internal.runners.ErrorReportingRunner;
import org.junit.runner.Runner;

/**
 * A RunnerBuilder is a strategy for constructing runners for classes. 
 
 * Only writers of custom runners should use <code>RunnerBuilder</code>s.  A custom runner class with a constructor taking
 * a <code>RunnerBuilder</code> parameter will be passed the instance of <code>RunnerBuilder</code> used to build that runner itself.  
 * For example,
 * imagine a custom runner that builds suites based on a list of classes in a text file:
 
 <pre>
 * \@RunWith(TextFileSuite.class)
 * \@SuiteSpecFile("mysuite.txt")
 * class MySuite {}
 </pre>
 
 * The implementation of TextFileSuite might include:
 
 <pre>
 * public TextFileSuite(Class testClass, RunnerBuilder builder) {
 *   // ...
 *   for (String className : readClassNames())
 *     addRunner(builder.runnerForClass(Class.forName(className)));
 *   // ...
 * }
 </pre>
 
 @see org.junit.runners.Suite
 */
public abstract class RunnerBuilder {
  private final Set<Class<?>> parents= new HashSet<Class<?>>();

  /**
   * Override to calculate the correct runner for a test class at runtime.
   
   @param testClass class to be run
   @return a Runner
   @throws Throwable if a runner cannot be constructed
   */
  public abstract Runner runnerForClass(Class<?> testClassthrows Throwable;

  /**
   * Always returns a runner, even if it is just one that prints an error instead of running tests.
   @param testClass class to be run
   @return a Runner
   */
  public Runner safeRunnerForClass(Class<?> testClass) {
    try {
      return runnerForClass(testClass);
    catch (Throwable e) {
      return new ErrorReportingRunner(testClass, e);
    }
  }

  Class<?> addParent(Class<?> parentthrows InitializationError {
    if (!parents.add(parent))
      throw new InitializationError(String.format("class '%s' (possibly indirectly) contains itself as a SuiteClass", parent.getName()));
    return parent;
  }

  void removeParent(Class<?> klass) {
    parents.remove(klass);
  }

  /**
   * Constructs and returns a list of Runners, one for each child class in
   * {@code children}.  Care is taken to avoid infinite recursion:
   * this builder will throw an exception if it is requested for another
   * runner for {@code parent} before this call completes.
   */
  public List<Runner> runners(Class<?> parent, Class<?>[] children)
      throws InitializationError {
    addParent(parent);

    try {
      return runners(children);
    finally {
      removeParent(parent);
    }
  }
  
  public List<Runner> runners(Class<?> parent, List<Class<?>> children)
      throws InitializationError {
    return runners(parent, children.toArray(new Class<?>[0]));
  }
  
  private List<Runner> runners(Class<?>[] children) {
    ArrayList<Runner> runners= new ArrayList<Runner>();
    for (Class<?> each : children) {
      Runner childRunner= safeRunnerForClass(each);
      if (childRunner != null)
        runners.add(childRunner);
    }
    return runners;
  }
}