• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package org.junit.runners.model;
2 
3 import java.util.ArrayList;
4 import java.util.HashSet;
5 import java.util.List;
6 import java.util.Set;
7 
8 import org.junit.internal.runners.ErrorReportingRunner;
9 import org.junit.runner.Runner;
10 
11 /**
12  * A RunnerBuilder is a strategy for constructing runners for classes.
13  *
14  * Only writers of custom runners should use <code>RunnerBuilder</code>s.  A custom runner class with a constructor taking
15  * a <code>RunnerBuilder</code> parameter will be passed the instance of <code>RunnerBuilder</code> used to build that runner itself.
16  * For example,
17  * imagine a custom runner that builds suites based on a list of classes in a text file:
18  *
19  * <pre>
20  * \@RunWith(TextFileSuite.class)
21  * \@SuiteSpecFile("mysuite.txt")
22  * class MySuite {}
23  * </pre>
24  *
25  * The implementation of TextFileSuite might include:
26  *
27  * <pre>
28  * public TextFileSuite(Class testClass, RunnerBuilder builder) {
29  *   // ...
30  *   for (String className : readClassNames())
31  *     addRunner(builder.runnerForClass(Class.forName(className)));
32  *   // ...
33  * }
34  * </pre>
35  *
36  * @see org.junit.runners.Suite
37  * @since 4.5
38  */
39 public abstract class RunnerBuilder {
40     private final Set<Class<?>> parents = new HashSet<Class<?>>();
41 
42     /**
43      * Override to calculate the correct runner for a test class at runtime.
44      *
45      * @param testClass class to be run
46      * @return a Runner
47      * @throws Throwable if a runner cannot be constructed
48      */
runnerForClass(Class<?> testClass)49     public abstract Runner runnerForClass(Class<?> testClass) throws Throwable;
50 
51     /**
52      * Always returns a runner, even if it is just one that prints an error instead of running tests.
53      *
54      * @param testClass class to be run
55      * @return a Runner
56      */
safeRunnerForClass(Class<?> testClass)57     public Runner safeRunnerForClass(Class<?> testClass) {
58         try {
59             return runnerForClass(testClass);
60         } catch (Throwable e) {
61             return new ErrorReportingRunner(testClass, e);
62         }
63     }
64 
addParent(Class<?> parent)65     Class<?> addParent(Class<?> parent) throws InitializationError {
66         if (!parents.add(parent)) {
67             throw new InitializationError(String.format("class '%s' (possibly indirectly) contains itself as a SuiteClass", parent.getName()));
68         }
69         return parent;
70     }
71 
removeParent(Class<?> klass)72     void removeParent(Class<?> klass) {
73         parents.remove(klass);
74     }
75 
76     /**
77      * Constructs and returns a list of Runners, one for each child class in
78      * {@code children}.  Care is taken to avoid infinite recursion:
79      * this builder will throw an exception if it is requested for another
80      * runner for {@code parent} before this call completes.
81      */
runners(Class<?> parent, Class<?>[] children)82     public List<Runner> runners(Class<?> parent, Class<?>[] children)
83             throws InitializationError {
84         addParent(parent);
85 
86         try {
87             return runners(children);
88         } finally {
89             removeParent(parent);
90         }
91     }
92 
runners(Class<?> parent, List<Class<?>> children)93     public List<Runner> runners(Class<?> parent, List<Class<?>> children)
94             throws InitializationError {
95         return runners(parent, children.toArray(new Class<?>[0]));
96     }
97 
runners(Class<?>[] children)98     private List<Runner> runners(Class<?>[] children) {
99         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 }
109