001/* 002 * (C) Copyright 2012-2013 Nuxeo SA (http://nuxeo.com/) and others. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 * 016 * Contributors: 017 * jcarsique 018 */ 019package org.nuxeo.runtime.test.runner; 020 021import java.lang.annotation.Annotation; 022import java.lang.annotation.ElementType; 023import java.lang.annotation.Inherited; 024import java.lang.annotation.Retention; 025import java.lang.annotation.RetentionPolicy; 026import java.lang.annotation.Target; 027import java.lang.reflect.Method; 028import java.lang.reflect.Modifier; 029import java.util.ArrayList; 030import java.util.Arrays; 031import java.util.HashMap; 032import java.util.List; 033import java.util.Map; 034 035import org.junit.runner.Description; 036import org.junit.runner.Runner; 037import org.junit.runner.notification.RunNotifier; 038import org.junit.runners.Parameterized.Parameters; 039import org.junit.runners.ParentRunner; 040import org.junit.runners.Suite.SuiteClasses; 041import org.junit.runners.model.FrameworkMethod; 042import org.junit.runners.model.InitializationError; 043import org.junit.runners.model.RunnerBuilder; 044import org.junit.runners.model.TestClass; 045 046/** 047 * JUnit4 ParentRunner that knows how to run a test class on multiple backend types. 048 * <p> 049 * To use it : 050 * 051 * <pre> 052 * @RunWith(ParameterizedSuite.class) 053 * @SuiteClasses(SimpleSession.class) 054 * @ParameterizedFeature(? extends RunnerFeature.class) 055 * public class NuxeoSuiteTest { 056 * @Parameters 057 * public static Collection<Object[]> yourParametersMethod() {...} 058 * } 059 * </pre> 060 * 061 * @ParameterizedFeature is optional. If used, the corresponding class must implement a method annotated with 062 * @ParameterizedMethod 063 */ 064public class ParameterizedSuite extends ParentRunner<FeaturesRunner> { 065 066 /** 067 * The <code>ParameterizedFeature</code> annotation specifies the class to be parameterized. That class must 068 * implement {@link RunnerFeature}. 069 */ 070 @Retention(RetentionPolicy.RUNTIME) 071 @Target(ElementType.TYPE) 072 @Inherited 073 public @interface ParameterizedFeature { 074 /** 075 * @return the class to be parameterized 076 */ 077 Class<?> value(); 078 } 079 080 @Retention(RetentionPolicy.RUNTIME) 081 @Target(ElementType.METHOD) 082 public @interface ParameterizedMethod { 083 } 084 085 @SuppressWarnings("unchecked") 086 private List<Object[]> getParametersList(TestClass klass) throws Throwable { 087 return (List<Object[]>) getParametersMethod(klass).invokeExplosively(null); 088 } 089 090 @SuppressWarnings("unchecked") 091 private Class<? extends RunnerFeature> getParameterizedClass(Class<?> klass) { 092 ParameterizedFeature annotation = klass.getAnnotation(ParameterizedFeature.class); 093 return (Class<? extends RunnerFeature>) (annotation != null ? annotation.value() : null); 094 } 095 096 private FrameworkMethod getParametersMethod(TestClass testClass) throws Exception { 097 List<FrameworkMethod> methods = testClass.getAnnotatedMethods(Parameters.class); 098 for (FrameworkMethod each : methods) { 099 int modifiers = each.getMethod().getModifiers(); 100 if (Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers)) 101 return each; 102 } 103 throw new Exception("Missing public static Parameters method on class " + testClass.getName()); 104 } 105 106 private final List<FeaturesRunner> runners = new ArrayList<FeaturesRunner>(); 107 108 private final Map<FeaturesRunner, Object[]> runnersParams = new HashMap<FeaturesRunner, Object[]>(); 109 110 private List<Object[]> parametersList; 111 112 private Class<? extends RunnerFeature> parameterizedClass; 113 114 public ParameterizedSuite(Class<?> testClass, RunnerBuilder builder) throws InitializationError { 115 this(builder, testClass, getSuiteClasses(testClass)); 116 } 117 118 public ParameterizedSuite(RunnerBuilder builder, Class<?> testClass, Class<?>[] classes) throws InitializationError { 119 super(testClass); 120 try { 121 this.parametersList = getParametersList(getTestClass()); 122 this.parameterizedClass = getParameterizedClass(testClass); 123 } catch (Throwable e) { 124 throw new InitializationError(e); 125 } 126 for (Object[] params : parametersList) { 127 List<Runner> runners2 = builder.runners(testClass, classes); 128 for (Runner runner : runners2) { 129 if (!(runner instanceof FeaturesRunner)) { 130 continue; 131 } 132 FeaturesRunner featureRunner = (FeaturesRunner) runner; 133 this.runners.add(featureRunner); 134 try { 135 if (parameterizedClass != null) { 136 runnersParams.put(featureRunner, params); 137 RunnerFeature feature = featureRunner.getFeature(parameterizedClass); 138 for (Method method : feature.getClass().getMethods()) { 139 if (method.getAnnotation(ParameterizedMethod.class) != null) { 140 method.invoke(feature, new Object[] { params }); 141 } 142 } 143 } 144 } catch (Throwable e) { 145 throw new InitializationError(e); 146 } 147 } 148 } 149 } 150 151 protected static Class<?>[] getSuiteClasses(Class<?> klass) throws InitializationError { 152 SuiteClasses annotation = klass.getAnnotation(SuiteClasses.class); 153 if (annotation == null) { 154 throw new InitializationError(String.format("class '%s' must have a SuiteClasses annotation", 155 klass.getName())); 156 } 157 return annotation.value(); 158 } 159 160 @Override 161 protected Description describeChild(FeaturesRunner child) { 162 Description description = child.getDescription(); 163 return Description.createTestDescription(description.getTestClass(), description.getDisplayName() + " " 164 + Arrays.toString(runnersParams.get(child)), description.getAnnotations().toArray(new Annotation[0])); 165 } 166 167 @Override 168 protected List<FeaturesRunner> getChildren() { 169 return runners; 170 } 171 172 @Override 173 protected void runChild(FeaturesRunner child, RunNotifier notifier) { 174 // for (Object[] params : parametersList) { 175 System.out.println(String.format("\r\n============= RUNNING %s =================", describeChild(child))); 176 // try { 177 // if (parameterizedClass != null) { 178 // RunnerFeature feature = child.getFeature(parameterizedClass); 179 // for (Method method : feature.getClass().getMethods()) { 180 // if (method.getAnnotation(ParameterizedMethod.class) != null) { 181 // method.invoke(feature, new Object[] { params }); 182 // } 183 // } 184 // } 185 child.run(notifier); 186 // } catch (Throwable e) { 187 // notifier.fireTestFailure(new Failure(child.getDescription(), e)); 188 // } 189 // } 190 } 191}