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. *

* If a runner supports inner member classes, the member classes will inherit the runner from the enclosing class, e.g.: *

 * @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
 * }
 * 
* The key points to note here are: * * * @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 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)); } } } }