/* * Copyright 2011 Harald Wellmann * * 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.ops4j.pax.exam.invoker.junit.internal; import java.lang.reflect.Field; import java.text.MessageFormat; import java.util.Collections; import java.util.Iterator; import java.util.List; import org.junit.runner.Runner; import org.junit.runner.notification.RunNotifier; import org.junit.runners.BlockJUnit4ClassRunner; import org.junit.runners.Parameterized.Parameter; import org.junit.runners.Parameterized.Parameters; import org.junit.runners.model.FrameworkField; import org.junit.runners.model.FrameworkMethod; import org.junit.runners.model.InitializationError; import org.ops4j.pax.exam.util.Injector; import org.osgi.framework.BundleContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * A JUnit {@link Runner} which is aware of an {@link Injector} and a {@link BundleContext} for * injecting dependencies from the OSGi service registry. * * @author Harald Wellmann * */ public class ParameterizedContainerTestRunner extends BlockJUnit4ClassRunner { private static final Logger LOG = LoggerFactory .getLogger(ParameterizedContainerTestRunner.class); private Injector injector; private Object[] parameters; /** * Constructs a runner for the given class which will be injected with dependencies from the * given bundle context by the given injector * * @param klass * test class to be run * @param injector * injector for injecting dependencies * @param index * parameter set index (counting from 0). * @throws InitializationError when test class cannot be initialized */ public ParameterizedContainerTestRunner(Class<?> klass, Injector injector, int index) throws InitializationError { super(klass); this.injector = injector; try { Iterator<Object[]> it = allParameters().iterator(); for (int i = 0; i <= index; i++) { parameters = it.next(); } } // CHECKSTYLE:SKIP - JUnit API catch (Throwable t) { throw new InitializationError(Collections.singletonList(t)); } } protected void validateConstructor(List<Throwable> errors) { validateOnlyOneConstructor(errors); } @Override protected Object createTest() throws Exception { Object test = null; if (fieldsAreAnnotated()) { test = createTestUsingFieldInjection(); } else { test = createTestUsingConstructorInjection(); } injector.injectFields(test); return test; } private Object createTestUsingConstructorInjection() throws Exception { return getTestClass().getOnlyConstructor().newInstance(parameters); } private Object createTestUsingFieldInjection() throws Exception { List<FrameworkField> annotatedFieldsByParameter = getAnnotatedFieldsByParameter(); if (annotatedFieldsByParameter.size() != parameters.length) { throw new Exception("Wrong number of parameters and @Parameter fields." + " @Parameter fields counted: " + annotatedFieldsByParameter.size() + ", available parameters: " + parameters.length + "."); } Object testClassInstance = getTestClass().getJavaClass().newInstance(); for (FrameworkField each : annotatedFieldsByParameter) { Field field = each.getField(); Parameter annotation = field.getAnnotation(Parameter.class); int index = annotation.value(); try { field.set(testClassInstance, parameters[index]); } catch (IllegalArgumentException iare) { throw new Exception(getTestClass().getName() + ": Trying to set " + field.getName() + " with the value " + parameters[index] + " that is not the right type (" + parameters[index].getClass().getSimpleName() + " instead of " + field.getType().getSimpleName() + ").", iare); } } return testClassInstance; } @Override protected void runChild(FrameworkMethod method, RunNotifier notifier) { LOG.info("running {} in reactor", method.getName()); super.runChild(method, notifier); } private List<FrameworkField> getAnnotatedFieldsByParameter() { return getTestClass().getAnnotatedFields(Parameter.class); } private boolean fieldsAreAnnotated() { return !getAnnotatedFieldsByParameter().isEmpty(); } @SuppressWarnings("unchecked") // CHECKSTYLE:SKIP - JUnit API private Iterable<Object[]> allParameters() throws Throwable { Object params = getParametersMethod().invokeExplosively(null); if (params instanceof Iterable) { return (Iterable<Object[]>) params; } else { throw parametersMethodReturnedWrongType(); } } private Exception parametersMethodReturnedWrongType() throws Exception { String className = getTestClass().getName(); String methodName = getParametersMethod().getName(); String message = MessageFormat.format("{0}.{1}() must return an Iterable of arrays.", className, methodName); return new Exception(message); } private FrameworkMethod getParametersMethod() throws Exception { List<FrameworkMethod> methods = getTestClass().getAnnotatedMethods(Parameters.class); for (FrameworkMethod each : methods) { if (each.isStatic() && each.isPublic()) { return each; } } throw new Exception("No public static parameters method on class " + getTestClass().getName()); } }