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 RunnerBuilders. A custom runner class with a constructor taking * a RunnerBuilder parameter will be passed the instance of RunnerBuilder 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: * *
 * \@RunWith(TextFileSuite.class)
 * \@SuiteSpecFile("mysuite.txt")
 * class MySuite {}
 * 
* * The implementation of TextFileSuite might include: * *
 * public TextFileSuite(Class testClass, RunnerBuilder builder) {
 *   // ...
 *   for (String className : readClassNames())
 *     addRunner(builder.runnerForClass(Class.forName(className)));
 *   // ...
 * }
 * 
* * @see org.junit.runners.Suite */ public abstract class RunnerBuilder { private final Set> parents= new HashSet>(); /** * 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 testClass) throws 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 parent) throws 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 runners(Class parent, Class[] children) throws InitializationError { addParent(parent); try { return runners(children); } finally { removeParent(parent); } } public List runners(Class parent, List> children) throws InitializationError { return runners(parent, children.toArray(new Class[0])); } private List runners(Class[] children) { ArrayList runners= new ArrayList(); for (Class each : children) { Runner childRunner= safeRunnerForClass(each); if (childRunner != null) runners.add(childRunner); } return runners; } }