/* * Copyright 2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.junit.internal.builders; import static java.util.Arrays.asList; import java.lang.reflect.Constructor; import java.lang.reflect.Modifier; import java.util.Arrays; import java.util.List; import org.junit.runner.RunWith; import org.junit.runner.Runner; import org.junit.runners.model.InitializationError; import org.junit.runners.model.RunnerBuilder; /** * <p> * Just like AnnotatedBuilder in JUnit4 except it supports running single * JDave behaviors. * </p> * <p> * If jdave-junit4 is in your classpath before junit4 then this will be loaded * first. * </p> * <p> * When running in your buildtool etc. you won't care about running a single behavior. * This class should be equivalent to the default JUnit4 one, so in that case it won't matter which one is loaded first. * </p> * * @author Tuomas Karkkainen */ public class AnnotatedBuilder extends RunnerBuilder { private static final String CONSTRUCTOR_ERROR_FORMAT = "Custom runner class %1$s should have a public constructor with signature %1$s(Class testClass) or %1$s(Class testClass, RunnerBuilder builder)"; private final RunnerBuilder suiteBuilder; public AnnotatedBuilder(final RunnerBuilder suiteBuilder) { this.suiteBuilder = suiteBuilder; } @Override public Runner runnerForClass(final Class<?> testClass) throws Exception { final RunWith annotation = getRunWithAnnotation(testClass); if (annotation != null) { return buildRunner(annotation.value(), testClass); } return null; } private RunWith getRunWithAnnotation(final Class<?> testClass) { if (testClass.isAnnotationPresent(RunWith.class)) { return testClass.getAnnotation(RunWith.class); } if (testClassIsANonStaticInnerClass(testClass)) { return testClass.getDeclaringClass().getAnnotation(RunWith.class); } return null; } private boolean testClassIsANonStaticInnerClass(final Class<?> testClass) { return testClass.isMemberClass() && !Modifier.isStatic(testClass.getModifiers()); } private <T extends Runner> T buildRunner(final Class<T> runnerClass, final Class<?> testClass) throws Exception { return new InternalBuilder<T>(runnerClass, suiteBuilder).build(testClass); } private static class InternalBuilder<T extends Runner> { private final RunnerBuilder suiteBuilder; private final Class<T> runnerClass; public InternalBuilder(final Class<T> runnerClass, final RunnerBuilder suiteBuilder) { this.runnerClass = runnerClass; this.suiteBuilder = suiteBuilder; } private T build(final Class<?> testClass) throws Exception { if (hasValidConstructor()) { return instantiateRunner(testClass); } throw new InitializationError(String.format(CONSTRUCTOR_ERROR_FORMAT, runnerClass .getSimpleName())); } private T instantiateRunner(final Class<?> testClass) throws Exception { if (hasSimpleConstructor()) { return getSimpleConstructor().newInstance(testClass); } return getConstructorWithSuite(runnerClass).newInstance(testClass, suiteBuilder); } private boolean hasValidConstructor() { return hasSimpleConstructor() || hasConstructorWithSuite(); } private boolean hasSimpleConstructor() { return getSimpleConstructor() != null; } private boolean hasConstructorWithSuite() { return getConstructorWithSuite(runnerClass) != null; } private Constructor<T> getSimpleConstructor() { for (final Constructor<T> constructor : getConstructors(runnerClass)) { final List<Class<?>> parameters = asList(constructor.getParameterTypes()); if (parameters.equals(Arrays.<Class<?>> asList(Class.class))) { return constructor; } } return null; } private Constructor<T> getConstructorWithSuite(final Class<T> runnerClass) { for (final Constructor<T> constructor : getConstructors(runnerClass)) { final List<Class<?>> parameters = asList(constructor.getParameterTypes()); if (parameters.equals(Arrays.<Class<?>> asList(Class.class, RunnerBuilder.class))) { return constructor; } } return null; } @SuppressWarnings("unchecked") private List<Constructor<T>> getConstructors(final Class<T> clazz) { return asList((Constructor<T>[]) clazz.getConstructors()); } } }