| package org.junit.internal.builders; |
| |
| import org.junit.runner.RunWith; |
| import org.junit.runner.Runner; |
| import org.junit.runners.model.InitializationError; |
| import org.junit.runners.model.RunnerBuilder; |
| |
| import java.lang.reflect.Modifier; |
| |
| |
| /** |
| * The {@code AnnotatedBuilder} is a strategy for constructing runners for test class that have been annotated with the |
| * {@code @RunWith} annotation. All tests within this class will be executed using the runner that was specified within |
| * the annotation. |
| * <p> |
| * If a runner supports inner member classes, the member classes will inherit the runner from the enclosing class, e.g.: |
| * <pre> |
| * @RunWith(MyRunner.class) |
| * public class MyTest { |
| * // some tests might go here |
| * |
| * public class MyMemberClass { |
| * @Test |
| * public void thisTestRunsWith_MyRunner() { |
| * // some test logic |
| * } |
| * |
| * // some more tests might go here |
| * } |
| * |
| * @RunWith(AnotherRunner.class) |
| * public class AnotherMemberClass { |
| * // some tests might go here |
| * |
| * public class DeepInnerClass { |
| * @Test |
| * public void thisTestRunsWith_AnotherRunner() { |
| * // some test logic |
| * } |
| * } |
| * |
| * public class DeepInheritedClass extends SuperTest { |
| * @Test |
| * public void thisTestRunsWith_SuperRunner() { |
| * // some test logic |
| * } |
| * } |
| * } |
| * } |
| * |
| * @RunWith(SuperRunner.class) |
| * public class SuperTest { |
| * // some tests might go here |
| * } |
| * </pre> |
| * The key points to note here are: |
| * <ul> |
| * <li>If there is no RunWith annotation, no runner will be created.</li> |
| * <li>The resolve step is inside-out, e.g. the closest RunWith annotation wins</li> |
| * <li>RunWith annotations are inherited and work as if the class was annotated itself.</li> |
| * <li>The default JUnit runner does not support inner member classes, |
| * so this is only valid for custom runners that support inner member classes.</li> |
| * <li>Custom runners with support for inner classes may or may not support RunWith annotations for member |
| * classes. Please refer to the custom runner documentation.</li> |
| * </ul> |
| * |
| * @see org.junit.runners.model.RunnerBuilder |
| * @see org.junit.runner.RunWith |
| * @since 4.0 |
| */ |
| public class AnnotatedBuilder extends RunnerBuilder { |
| private static final String CONSTRUCTOR_ERROR_FORMAT = "Custom runner class %s should have a public constructor with signature %s(Class testClass)"; |
| |
| private final RunnerBuilder suiteBuilder; |
| |
| public AnnotatedBuilder(RunnerBuilder suiteBuilder) { |
| this.suiteBuilder = suiteBuilder; |
| } |
| |
| @Override |
| public Runner runnerForClass(Class<?> testClass) throws Exception { |
| for (Class<?> currentTestClass = testClass; currentTestClass != null; |
| currentTestClass = getEnclosingClassForNonStaticMemberClass(currentTestClass)) { |
| RunWith annotation = currentTestClass.getAnnotation(RunWith.class); |
| if (annotation != null) { |
| return buildRunner(annotation.value(), testClass); |
| } |
| } |
| |
| return null; |
| } |
| |
| private Class<?> getEnclosingClassForNonStaticMemberClass(Class<?> currentTestClass) { |
| if (currentTestClass.isMemberClass() && !Modifier.isStatic(currentTestClass.getModifiers())) { |
| return currentTestClass.getEnclosingClass(); |
| } else { |
| return null; |
| } |
| } |
| |
| public Runner buildRunner(Class<? extends Runner> runnerClass, |
| Class<?> testClass) throws Exception { |
| try { |
| return runnerClass.getConstructor(Class.class).newInstance(testClass); |
| } catch (NoSuchMethodException e) { |
| try { |
| return runnerClass.getConstructor(Class.class, |
| RunnerBuilder.class).newInstance(testClass, suiteBuilder); |
| } catch (NoSuchMethodException e2) { |
| String simpleName = runnerClass.getSimpleName(); |
| throw new InitializationError(String.format( |
| CONSTRUCTOR_ERROR_FORMAT, simpleName, simpleName)); |
| } |
| } |
| } |
| } |