001    package org.junit.runners.model;
002    
003    import java.util.ArrayList;
004    import java.util.HashSet;
005    import java.util.List;
006    import java.util.Set;
007    
008    import org.junit.internal.runners.ErrorReportingRunner;
009    import org.junit.runner.Runner;
010    
011    /**
012     * A RunnerBuilder is a strategy for constructing runners for classes.
013     *
014     * Only writers of custom runners should use <code>RunnerBuilder</code>s.  A custom runner class with a constructor taking
015     * a <code>RunnerBuilder</code> parameter will be passed the instance of <code>RunnerBuilder</code> used to build that runner itself.
016     * For example,
017     * imagine a custom runner that builds suites based on a list of classes in a text file:
018     *
019     * <pre>
020     * \@RunWith(TextFileSuite.class)
021     * \@SuiteSpecFile("mysuite.txt")
022     * class MySuite {}
023     * </pre>
024     *
025     * The implementation of TextFileSuite might include:
026     *
027     * <pre>
028     * public TextFileSuite(Class testClass, RunnerBuilder builder) {
029     *   // ...
030     *   for (String className : readClassNames())
031     *     addRunner(builder.runnerForClass(Class.forName(className)));
032     *   // ...
033     * }
034     * </pre>
035     *
036     * @see org.junit.runners.Suite
037     * @since 4.5
038     */
039    public abstract class RunnerBuilder {
040        private final Set<Class<?>> parents = new HashSet<Class<?>>();
041    
042        /**
043         * Override to calculate the correct runner for a test class at runtime.
044         *
045         * @param testClass class to be run
046         * @return a Runner
047         * @throws Throwable if a runner cannot be constructed
048         */
049        public abstract Runner runnerForClass(Class<?> testClass) throws Throwable;
050    
051        /**
052         * Always returns a runner, even if it is just one that prints an error instead of running tests.
053         *
054         * @param testClass class to be run
055         * @return a Runner
056         */
057        public Runner safeRunnerForClass(Class<?> testClass) {
058            try {
059                return runnerForClass(testClass);
060            } catch (Throwable e) {
061                return new ErrorReportingRunner(testClass, e);
062            }
063        }
064    
065        Class<?> addParent(Class<?> parent) throws InitializationError {
066            if (!parents.add(parent)) {
067                throw new InitializationError(String.format("class '%s' (possibly indirectly) contains itself as a SuiteClass", parent.getName()));
068            }
069            return parent;
070        }
071    
072        void removeParent(Class<?> klass) {
073            parents.remove(klass);
074        }
075    
076        /**
077         * Constructs and returns a list of Runners, one for each child class in
078         * {@code children}.  Care is taken to avoid infinite recursion:
079         * this builder will throw an exception if it is requested for another
080         * runner for {@code parent} before this call completes.
081         */
082        public List<Runner> runners(Class<?> parent, Class<?>[] children)
083                throws InitializationError {
084            addParent(parent);
085    
086            try {
087                return runners(children);
088            } finally {
089                removeParent(parent);
090            }
091        }
092    
093        public List<Runner> runners(Class<?> parent, List<Class<?>> children)
094                throws InitializationError {
095            return runners(parent, children.toArray(new Class<?>[0]));
096        }
097    
098        private List<Runner> runners(Class<?>[] children) {
099            ArrayList<Runner> runners = new ArrayList<Runner>();
100            for (Class<?> each : children) {
101                Runner childRunner = safeRunnerForClass(each);
102                if (childRunner != null) {
103                    runners.add(childRunner);
104                }
105            }
106            return runners;
107        }
108    }