/* * (C) Copyright 2012-2013 Nuxeo SA (http://nuxeo.com/) and others. * * 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. * * Contributors: * jcarsique */ package org.nuxeo.runtime.test.runner; import java.lang.annotation.Annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import org.junit.runner.Description; import org.junit.runner.Runner; import org.junit.runner.notification.RunNotifier; import org.junit.runners.Parameterized.Parameters; import org.junit.runners.ParentRunner; import org.junit.runners.Suite.SuiteClasses; import org.junit.runners.model.FrameworkMethod; import org.junit.runners.model.InitializationError; import org.junit.runners.model.RunnerBuilder; import org.junit.runners.model.TestClass; /** * JUnit4 ParentRunner that knows how to run a test class on multiple backend types. * <p> * To use it : * * <pre> * @RunWith(ParameterizedSuite.class) * @SuiteClasses(SimpleSession.class) * @ParameterizedFeature(? extends RunnerFeature.class) * public class NuxeoSuiteTest { * @Parameters * public static Collection<Object[]> yourParametersMethod() {...} * } * </pre> * * @ParameterizedFeature is optional. If used, the corresponding class must implement a method annotated with * @ParameterizedMethod */ public class ParameterizedSuite extends ParentRunner<FeaturesRunner> { /** * The <code>ParameterizedFeature</code> annotation specifies the class to be parameterized. That class must extend * {@link SimpleFeature} or implement {@link RunnerFeature}. */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Inherited public @interface ParameterizedFeature { /** * @return the class to be parameterized */ public Class<?> value(); } @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public static @interface ParameterizedMethod { } @SuppressWarnings("unchecked") private List<Object[]> getParametersList(TestClass klass) throws Throwable { return (List<Object[]>) getParametersMethod(klass).invokeExplosively(null); } @SuppressWarnings("unchecked") private Class<? extends RunnerFeature> getParameterizedClass(Class<?> klass) throws Throwable { ParameterizedFeature annotation = klass.getAnnotation(ParameterizedFeature.class); return (Class<? extends RunnerFeature>) (annotation != null ? annotation.value() : null); } private FrameworkMethod getParametersMethod(TestClass testClass) throws Exception { List<FrameworkMethod> methods = testClass.getAnnotatedMethods(Parameters.class); for (FrameworkMethod each : methods) { int modifiers = each.getMethod().getModifiers(); if (Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers)) return each; } throw new Exception("Missing public static Parameters method on class " + testClass.getName()); } private final List<FeaturesRunner> runners = new ArrayList<FeaturesRunner>(); private final Map<FeaturesRunner, Object[]> runnersParams = new HashMap<FeaturesRunner, Object[]>(); private List<Object[]> parametersList; private Class<? extends RunnerFeature> parameterizedClass; public ParameterizedSuite(Class<?> testClass, RunnerBuilder builder) throws InitializationError { this(builder, testClass, getSuiteClasses(testClass)); } public ParameterizedSuite(RunnerBuilder builder, Class<?> testClass, Class<?>[] classes) throws InitializationError { super(testClass); try { this.parametersList = getParametersList(getTestClass()); this.parameterizedClass = getParameterizedClass(testClass); } catch (Throwable e) { throw new InitializationError(e); } for (Object[] params : parametersList) { List<Runner> runners2 = builder.runners(testClass, classes); for (Runner runner : runners2) { if (!(runner instanceof FeaturesRunner)) { continue; } FeaturesRunner featureRunner = (FeaturesRunner) runner; this.runners.add(featureRunner); try { if (parameterizedClass != null) { runnersParams.put(featureRunner, params); RunnerFeature feature = featureRunner.getFeature(parameterizedClass); for (Method method : feature.getClass().getMethods()) { if (method.getAnnotation(ParameterizedMethod.class) != null) { method.invoke(feature, new Object[] { params }); } } } } catch (Throwable e) { throw new InitializationError(e); } } } } protected static Class<?>[] getSuiteClasses(Class<?> klass) throws InitializationError { SuiteClasses annotation = klass.getAnnotation(SuiteClasses.class); if (annotation == null) { throw new InitializationError(String.format("class '%s' must have a SuiteClasses annotation", klass.getName())); } return annotation.value(); } @Override protected Description describeChild(FeaturesRunner child) { Description description = child.getDescription(); return Description.createTestDescription(description.getTestClass(), description.getDisplayName() + " " + Arrays.toString(runnersParams.get(child)), (Annotation[]) description.getAnnotations().toArray()); } @Override protected List<FeaturesRunner> getChildren() { return runners; } @Override protected void runChild(FeaturesRunner child, RunNotifier notifier) { // for (Object[] params : parametersList) { System.out.println(String.format("\r\n============= RUNNING %s =================", describeChild(child))); // try { // if (parameterizedClass != null) { // RunnerFeature feature = child.getFeature(parameterizedClass); // for (Method method : feature.getClass().getMethods()) { // if (method.getAnnotation(ParameterizedMethod.class) != null) { // method.invoke(feature, new Object[] { params }); // } // } // } child.run(notifier); // } catch (Throwable e) { // notifier.fireTestFailure(new Failure(child.getDescription(), e)); // } // } } }