• 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.Description;
10 import org.junit.runner.OrderWith;
11 import org.junit.runner.Runner;
12 import org.junit.runner.manipulation.InvalidOrderingException;
13 import org.junit.runner.manipulation.Ordering;
14 
15 /**
16  * A RunnerBuilder is a strategy for constructing runners for classes.
17  *
18  * Only writers of custom runners should use <code>RunnerBuilder</code>s.  A custom runner class with a constructor taking
19  * a <code>RunnerBuilder</code> parameter will be passed the instance of <code>RunnerBuilder</code> used to build that runner itself.
20  * For example,
21  * imagine a custom runner that builds suites based on a list of classes in a text file:
22  *
23  * <pre>
24  * \@RunWith(TextFileSuite.class)
25  * \@SuiteSpecFile("mysuite.txt")
26  * class MySuite {}
27  * </pre>
28  *
29  * The implementation of TextFileSuite might include:
30  *
31  * <pre>
32  * public TextFileSuite(Class testClass, RunnerBuilder builder) {
33  *   // ...
34  *   for (String className : readClassNames())
35  *     addRunner(builder.runnerForClass(Class.forName(className)));
36  *   // ...
37  * }
38  * </pre>
39  *
40  * @see org.junit.runners.Suite
41  * @since 4.5
42  */
43 public abstract class RunnerBuilder {
44     private final Set<Class<?>> parents = new HashSet<Class<?>>();
45 
46     /**
47      * Override to calculate the correct runner for a test class at runtime.
48      *
49      * @param testClass class to be run
50      * @return a Runner
51      * @throws Throwable if a runner cannot be constructed
52      */
runnerForClass(Class<?> testClass)53     public abstract Runner runnerForClass(Class<?> testClass) throws Throwable;
54 
55     /**
56      * Always returns a runner for the given test class.
57      *
58      * <p>In case of an exception a runner will be returned that prints an error instead of running
59      * tests.
60      *
61      * <p>Note that some of the internal JUnit implementations of RunnerBuilder will return
62      * {@code null} from this method, but no RunnerBuilder passed to a Runner constructor will
63      * return {@code null} from this method.
64      *
65      * @param testClass class to be run
66      * @return a Runner
67      */
safeRunnerForClass(Class<?> testClass)68     public Runner safeRunnerForClass(Class<?> testClass) {
69         try {
70             Runner runner = runnerForClass(testClass);
71             if (runner != null) {
72                 configureRunner(runner);
73             }
74             return runner;
75         } catch (Throwable e) {
76             return new ErrorReportingRunner(testClass, e);
77         }
78     }
79 
configureRunner(Runner runner)80     private void configureRunner(Runner runner) throws InvalidOrderingException {
81         Description description = runner.getDescription();
82         OrderWith orderWith = description.getAnnotation(OrderWith.class);
83         if (orderWith != null) {
84             Ordering ordering = Ordering.definedBy(orderWith.value(), description);
85             ordering.apply(runner);
86         }
87     }
88 
addParent(Class<?> parent)89     Class<?> addParent(Class<?> parent) throws InitializationError {
90         if (!parents.add(parent)) {
91             throw new InitializationError(String.format("class '%s' (possibly indirectly) contains itself as a SuiteClass", parent.getName()));
92         }
93         return parent;
94     }
95 
removeParent(Class<?> klass)96     void removeParent(Class<?> klass) {
97         parents.remove(klass);
98     }
99 
100     /**
101      * Constructs and returns a list of Runners, one for each child class in
102      * {@code children}.  Care is taken to avoid infinite recursion:
103      * this builder will throw an exception if it is requested for another
104      * runner for {@code parent} before this call completes.
105      */
runners(Class<?> parent, Class<?>[] children)106     public List<Runner> runners(Class<?> parent, Class<?>[] children)
107             throws InitializationError {
108         addParent(parent);
109 
110         try {
111             return runners(children);
112         } finally {
113             removeParent(parent);
114         }
115     }
116 
runners(Class<?> parent, List<Class<?>> children)117     public List<Runner> runners(Class<?> parent, List<Class<?>> children)
118             throws InitializationError {
119         return runners(parent, children.toArray(new Class<?>[0]));
120     }
121 
runners(Class<?>[] children)122     private List<Runner> runners(Class<?>[] children) {
123         List<Runner> runners = new ArrayList<Runner>();
124         for (Class<?> each : children) {
125             Runner childRunner = safeRunnerForClass(each);
126             if (childRunner != null) {
127                 runners.add(childRunner);
128             }
129         }
130         return runners;
131     }
132 }
133