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