aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/org/junit/runners/model/RunnerBuilder.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/org/junit/runners/model/RunnerBuilder.java')
-rw-r--r--src/main/java/org/junit/runners/model/RunnerBuilder.java104
1 files changed, 104 insertions, 0 deletions
diff --git a/src/main/java/org/junit/runners/model/RunnerBuilder.java b/src/main/java/org/junit/runners/model/RunnerBuilder.java
new file mode 100644
index 0000000..3a334be
--- /dev/null
+++ b/src/main/java/org/junit/runners/model/RunnerBuilder.java
@@ -0,0 +1,104 @@
+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<?> 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<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;
+ }
+}