package org.junit.runners;
import static org.junit.internal.runners.rules.
RuleMemberValidator.
CLASS_RULE_METHOD_VALIDATOR;
import static org.junit.internal.runners.rules.
RuleMemberValidator.
CLASS_RULE_VALIDATOR;
import java.lang.annotation.
Annotation;
import java.lang.reflect.
Method;
import java.util.
ArrayList;
import java.util.
Arrays;
import java.util.
Collection;
import java.util.
Collections;
import java.util.
Comparator;
import java.util.
Iterator;
import java.util.
List;
import org.junit.
AfterClass;
import org.junit.
BeforeClass;
import org.junit.
ClassRule;
import org.junit.
Ignore;
import org.junit.
Rule;
import org.junit.internal.
AssumptionViolatedException;
import org.junit.internal.runners.model.
EachTestNotifier;
import org.junit.internal.runners.statements.
RunAfters;
import org.junit.internal.runners.statements.
RunBefores;
import org.junit.rules.
RunRules;
import org.junit.rules.
TestRule;
import org.junit.runner.
Description;
import org.junit.runner.
Runner;
import org.junit.runner.manipulation.
Filter;
import org.junit.runner.manipulation.
Filterable;
import org.junit.runner.manipulation.
NoTestsRemainException;
import org.junit.runner.manipulation.
Sortable;
import org.junit.runner.manipulation.
Sorter;
import org.junit.runner.notification.
RunNotifier;
import org.junit.runner.notification.
StoppedByUserException;
import org.junit.runners.model.
FrameworkMethod;
import org.junit.runners.model.
InitializationError;
import org.junit.runners.model.
RunnerScheduler;
import org.junit.runners.model.
Statement;
import org.junit.runners.model.
TestClass;
import org.junit.validator.
AnnotationsValidator;
import org.junit.validator.
PublicClassValidator;
import org.junit.validator.
TestClassValidator;
/**
* Provides most of the functionality specific to a Runner that implements a
* "parent node" in the test tree, with children defined by objects of some data
* type {@code T}. (For {@link BlockJUnit4ClassRunner}, {@code T} is
* {@link Method} . For {@link Suite}, {@code T} is {@link Class}.) Subclasses
* must implement finding the children of the node, describing each child, and
* running each child. ParentRunner will filter and sort children, handle
* {@code @BeforeClass} and {@code @AfterClass} methods,
* handle annotated {@link ClassRule}s, create a composite
* {@link Description}, and run children sequentially.
*
* @since 4.5
*/
public abstract class
ParentRunner<T> extends
Runner implements
Filterable,
Sortable {
private static final
List<
TestClassValidator>
VALIDATORS =
Arrays.
asList(
new
AnnotationsValidator(), new
PublicClassValidator());
private final
Object childrenLock = new
Object();
private final
TestClass testClass;
// Guarded by childrenLock
private volatile
Collection<T>
filteredChildren = null;
private volatile
RunnerScheduler scheduler = new
RunnerScheduler() {
public void
schedule(
Runnable childStatement) {
childStatement.
run();
}
public void
finished() {
// do nothing
}
};
/**
* Constructs a new {@code ParentRunner} that will run {@code @TestClass}
*/
protected
ParentRunner(
Class<?>
testClass) throws
InitializationError {
this.
testClass =
createTestClass(
testClass);
validate();
}
protected
TestClass createTestClass(
Class<?>
testClass) {
return new
TestClass(
testClass);
}
//
// Must be overridden
//
/**
* Returns a list of objects that define the children of this Runner.
*/
protected abstract
List<T>
getChildren();
/**
* Returns a {@link Description} for {@code child}, which can be assumed to
* be an element of the list returned by {@link ParentRunner#getChildren()}
*/
protected abstract
Description describeChild(T
child);
/**
* Runs the test corresponding to {@code child}, which can be assumed to be
* an element of the list returned by {@link ParentRunner#getChildren()}.
* Subclasses are responsible for making sure that relevant test events are
* reported through {@code notifier}
*/
protected abstract void
runChild(T
child,
RunNotifier notifier);
//
// May be overridden
//
/**
* Adds to {@code errors} a throwable for each problem noted with the test class (available from {@link #getTestClass()}).
* Default implementation adds an error for each method annotated with
* {@code @BeforeClass} or {@code @AfterClass} that is not
* {@code public static void} with no arguments.
*/
protected void
collectInitializationErrors(
List<
Throwable>
errors) {
validatePublicVoidNoArgMethods(
BeforeClass.class, true,
errors);
validatePublicVoidNoArgMethods(
AfterClass.class, true,
errors);
validateClassRules(
errors);
applyValidators(
errors);
}
private void
applyValidators(
List<
Throwable>
errors) {
if (
getTestClass().
getJavaClass() != null) {
for (
TestClassValidator each :
VALIDATORS) {
errors.
addAll(
each.
validateTestClass(
getTestClass()));
}
}
}
/**
* Adds to {@code errors} if any method in this class is annotated with
* {@code annotation}, but:
* <ul>
* <li>is not public, or
* <li>takes parameters, or
* <li>returns something other than void, or
* <li>is static (given {@code isStatic is false}), or
* <li>is not static (given {@code isStatic is true}).
* </ul>
*/
protected void
validatePublicVoidNoArgMethods(
Class<? extends
Annotation>
annotation,
boolean
isStatic,
List<
Throwable>
errors) {
List<
FrameworkMethod>
methods =
getTestClass().
getAnnotatedMethods(
annotation);
for (
FrameworkMethod eachTestMethod :
methods) {
eachTestMethod.
validatePublicVoidNoArg(
isStatic,
errors);
}
}
private void
validateClassRules(
List<
Throwable>
errors) {
CLASS_RULE_VALIDATOR.
validate(
getTestClass(),
errors);
CLASS_RULE_METHOD_VALIDATOR.
validate(
getTestClass(),
errors);
}
/**
* Constructs a {@code Statement} to run all of the tests in the test class.
* Override to add pre-/post-processing. Here is an outline of the
* implementation:
* <ol>
* <li>Determine the children to be run using {@link #getChildren()}
* (subject to any imposed filter and sort).</li>
* <li>If there are any children remaining after filtering and ignoring,
* construct a statement that will:
* <ol>
* <li>Apply all {@code ClassRule}s on the test-class and superclasses.</li>
* <li>Run all non-overridden {@code @BeforeClass} methods on the test-class
* and superclasses; if any throws an Exception, stop execution and pass the
* exception on.</li>
* <li>Run all remaining tests on the test-class.</li>
* <li>Run all non-overridden {@code @AfterClass} methods on the test-class
* and superclasses: exceptions thrown by previous steps are combined, if
* necessary, with exceptions from AfterClass methods into a
* {@link org.junit.runners.model.MultipleFailureException}.</li>
* </ol>
* </li>
* </ol>
*
* @return {@code Statement}
*/
protected
Statement classBlock(final
RunNotifier notifier) {
Statement statement =
childrenInvoker(
notifier);
if (!
areAllChildrenIgnored()) {
statement =
withBeforeClasses(
statement);
statement =
withAfterClasses(
statement);
statement =
withClassRules(
statement);
}
return
statement;
}
private boolean
areAllChildrenIgnored() {
for (T
child :
getFilteredChildren()) {
if (!
isIgnored(
child)) {
return false;
}
}
return true;
}
/**
* Returns a {@link Statement}: run all non-overridden {@code @BeforeClass} methods on this class
* and superclasses before executing {@code statement}; if any throws an
* Exception, stop execution and pass the exception on.
*/
protected
Statement withBeforeClasses(
Statement statement) {
List<
FrameworkMethod>
befores =
testClass
.
getAnnotatedMethods(
BeforeClass.class);
return
befores.
isEmpty() ?
statement :
new
RunBefores(
statement,
befores, null);
}
/**
* Returns a {@link Statement}: run all non-overridden {@code @AfterClass} methods on this class
* and superclasses before executing {@code statement}; all AfterClass methods are
* always executed: exceptions thrown by previous steps are combined, if
* necessary, with exceptions from AfterClass methods into a
* {@link org.junit.runners.model.MultipleFailureException}.
*/
protected
Statement withAfterClasses(
Statement statement) {
List<
FrameworkMethod>
afters =
testClass
.
getAnnotatedMethods(
AfterClass.class);
return
afters.
isEmpty() ?
statement :
new
RunAfters(
statement,
afters, null);
}
/**
* Returns a {@link Statement}: apply all
* static fields assignable to {@link TestRule}
* annotated with {@link ClassRule}.
*
* @param statement the base statement
* @return a RunRules statement if any class-level {@link Rule}s are
* found, or the base statement
*/
private
Statement withClassRules(
Statement statement) {
List<
TestRule>
classRules =
classRules();
return
classRules.
isEmpty() ?
statement :
new
RunRules(
statement,
classRules,
getDescription());
}
/**
* @return the {@code ClassRule}s that can transform the block that runs
* each method in the tested class.
*/
protected
List<
TestRule>
classRules() {
List<
TestRule>
result =
testClass.
getAnnotatedMethodValues(null,
ClassRule.class,
TestRule.class);
result.
addAll(
testClass.
getAnnotatedFieldValues(null,
ClassRule.class,
TestRule.class));
return
result;
}
/**
* Returns a {@link Statement}: Call {@link #runChild(Object, RunNotifier)}
* on each object returned by {@link #getChildren()} (subject to any imposed
* filter and sort)
*/
protected
Statement childrenInvoker(final
RunNotifier notifier) {
return new
Statement() {
@
Override
public void
evaluate() {
runChildren(
notifier);
}
};
}
/**
* Evaluates whether a child is ignored. The default implementation always
* returns <code>false</code>.
*
* <p>{@link BlockJUnit4ClassRunner}, for example, overrides this method to
* filter tests based on the {@link Ignore} annotation.
*/
protected boolean
isIgnored(T
child) {
return false;
}
private void
runChildren(final
RunNotifier notifier) {
final
RunnerScheduler currentScheduler =
scheduler;
try {
for (final T
each :
getFilteredChildren()) {
currentScheduler.
schedule(new
Runnable() {
public void
run() {
ParentRunner.this.
runChild(
each,
notifier);
}
});
}
} finally {
currentScheduler.
finished();
}
}
/**
* Returns a name used to describe this Runner
*/
protected
String getName() {
return
testClass.
getName();
}
//
// Available for subclasses
//
/**
* Returns a {@link TestClass} object wrapping the class to be executed.
*/
public final
TestClass getTestClass() {
return
testClass;
}
/**
* Runs a {@link Statement} that represents a leaf (aka atomic) test.
*/
protected final void
runLeaf(
Statement statement,
Description description,
RunNotifier notifier) {
EachTestNotifier eachNotifier = new
EachTestNotifier(
notifier,
description);
eachNotifier.
fireTestStarted();
try {
statement.
evaluate();
} catch (
AssumptionViolatedException e) {
eachNotifier.
addFailedAssumption(
e);
} catch (
Throwable e) {
eachNotifier.
addFailure(
e);
} finally {
eachNotifier.
fireTestFinished();
}
}
/**
* @return the annotations that should be attached to this runner's
* description.
*/
protected
Annotation[]
getRunnerAnnotations() {
return
testClass.
getAnnotations();
}
//
// Implementation of Runner
//
@
Override
public
Description getDescription() {
Description description =
Description.
createSuiteDescription(
getName(),
getRunnerAnnotations());
for (T
child :
getFilteredChildren()) {
description.
addChild(
describeChild(
child));
}
return
description;
}
@
Override
public void
run(final
RunNotifier notifier) {
EachTestNotifier testNotifier = new
EachTestNotifier(
notifier,
getDescription());
try {
Statement statement =
classBlock(
notifier);
statement.
evaluate();
} catch (
AssumptionViolatedException e) {
testNotifier.
addFailedAssumption(
e);
} catch (
StoppedByUserException e) {
throw
e;
} catch (
Throwable e) {
testNotifier.
addFailure(
e);
}
}
//
// Implementation of Filterable and Sortable
//
public void
filter(
Filter filter) throws
NoTestsRemainException {
synchronized (
childrenLock) {
List<T>
children = new
ArrayList<T>(
getFilteredChildren());
for (
Iterator<T>
iter =
children.
iterator();
iter.
hasNext(); ) {
T
each =
iter.
next();
if (
shouldRun(
filter,
each)) {
try {
filter.
apply(
each);
} catch (
NoTestsRemainException e) {
iter.
remove();
}
} else {
iter.
remove();
}
}
filteredChildren =
Collections.
unmodifiableCollection(
children);
if (
filteredChildren.
isEmpty()) {
throw new
NoTestsRemainException();
}
}
}
public void
sort(
Sorter sorter) {
synchronized (
childrenLock) {
for (T
each :
getFilteredChildren()) {
sorter.
apply(
each);
}
List<T>
sortedChildren = new
ArrayList<T>(
getFilteredChildren());
Collections.
sort(
sortedChildren,
comparator(
sorter));
filteredChildren =
Collections.
unmodifiableCollection(
sortedChildren);
}
}
//
// Private implementation
//
private void
validate() throws
InitializationError {
List<
Throwable>
errors = new
ArrayList<
Throwable>();
collectInitializationErrors(
errors);
if (!
errors.
isEmpty()) {
throw new
InitializationError(
errors);
}
}
private
Collection<T>
getFilteredChildren() {
if (
filteredChildren == null) {
synchronized (
childrenLock) {
if (
filteredChildren == null) {
filteredChildren =
Collections.
unmodifiableCollection(
getChildren());
}
}
}
return
filteredChildren;
}
private boolean
shouldRun(
Filter filter, T
each) {
return
filter.
shouldRun(
describeChild(
each));
}
private
Comparator<? super T>
comparator(final
Sorter sorter) {
return new
Comparator<T>() {
public int
compare(T
o1, T
o2) {
return
sorter.
compare(
describeChild(
o1),
describeChild(
o2));
}
};
}
/**
* Sets a scheduler that determines the order and parallelization
* of children. Highly experimental feature that may change.
*/
public void
setScheduler(
RunnerScheduler scheduler) {
this.
scheduler =
scheduler;
}
}