001    package org.junit.runners;
002    
003    import java.lang.annotation.Annotation;
004    import java.lang.annotation.ElementType;
005    import java.lang.annotation.Inherited;
006    import java.lang.annotation.Retention;
007    import java.lang.annotation.RetentionPolicy;
008    import java.lang.annotation.Target;
009    import java.text.MessageFormat;
010    import java.util.ArrayList;
011    import java.util.Arrays;
012    import java.util.Collection;
013    import java.util.Collections;
014    import java.util.List;
015    
016    import org.junit.internal.AssumptionViolatedException;
017    import org.junit.runner.Description;
018    import org.junit.runner.Result;
019    import org.junit.runner.Runner;
020    import org.junit.runner.notification.Failure;
021    import org.junit.runner.notification.RunNotifier;
022    import org.junit.runners.model.FrameworkMethod;
023    import org.junit.runners.model.InvalidTestClassError;
024    import org.junit.runners.model.TestClass;
025    import org.junit.runners.parameterized.BlockJUnit4ClassRunnerWithParametersFactory;
026    import org.junit.runners.parameterized.ParametersRunnerFactory;
027    import org.junit.runners.parameterized.TestWithParameters;
028    
029    /**
030     * The custom runner <code>Parameterized</code> implements parameterized tests.
031     * When running a parameterized test class, instances are created for the
032     * cross-product of the test methods and the test data elements.
033     * <p>
034     * For example, to test the <code>+</code> operator, write:
035     * <pre>
036     * &#064;RunWith(Parameterized.class)
037     * public class AdditionTest {
038     *     &#064;Parameters(name = &quot;{index}: {0} + {1} = {2}&quot;)
039     *     public static Iterable&lt;Object[]&gt; data() {
040     *         return Arrays.asList(new Object[][] { { 0, 0, 0 }, { 1, 1, 2 },
041     *                 { 3, 2, 5 }, { 4, 3, 7 } });
042     *     }
043     *
044     *     private int firstSummand;
045     *
046     *     private int secondSummand;
047     *
048     *     private int sum;
049     *
050     *     public AdditionTest(int firstSummand, int secondSummand, int sum) {
051     *         this.firstSummand = firstSummand;
052     *         this.secondSummand = secondSummand;
053     *         this.sum = sum;
054     *     }
055     *
056     *     &#064;Test
057     *     public void test() {
058     *         assertEquals(sum, firstSummand + secondSummand);
059     *     }
060     * }
061     * </pre>
062     * <p>
063     * Each instance of <code>AdditionTest</code> will be constructed using the
064     * three-argument constructor and the data values in the
065     * <code>&#064;Parameters</code> method.
066     * <p>
067     * In order that you can easily identify the individual tests, you may provide a
068     * name for the <code>&#064;Parameters</code> annotation. This name is allowed
069     * to contain placeholders, which are replaced at runtime. The placeholders are
070     * <dl>
071     * <dt>{index}</dt>
072     * <dd>the current parameter index</dd>
073     * <dt>{0}</dt>
074     * <dd>the first parameter value</dd>
075     * <dt>{1}</dt>
076     * <dd>the second parameter value</dd>
077     * <dt>...</dt>
078     * <dd>...</dd>
079     * </dl>
080     * <p>
081     * In the example given above, the <code>Parameterized</code> runner creates
082     * names like <code>[2: 3 + 2 = 5]</code>. If you don't use the name parameter,
083     * then the current parameter index is used as name.
084     * <p>
085     * You can also write:
086     * <pre>
087     * &#064;RunWith(Parameterized.class)
088     * public class AdditionTest {
089     *     &#064;Parameters(name = &quot;{index}: {0} + {1} = {2}&quot;)
090     *     public static Iterable&lt;Object[]&gt; data() {
091     *         return Arrays.asList(new Object[][] { { 0, 0, 0 }, { 1, 1, 2 },
092     *                 { 3, 2, 5 }, { 4, 3, 7 } });
093     *     }
094     *
095     *     &#064;Parameter(0)
096     *     public int firstSummand;
097     *
098     *     &#064;Parameter(1)
099     *     public int secondSummand;
100     *
101     *     &#064;Parameter(2)
102     *     public int sum;
103     *
104     *     &#064;Test
105     *     public void test() {
106     *         assertEquals(sum, firstSummand + secondSummand);
107     *     }
108     * }
109     * </pre>
110     * <p>
111     * Each instance of <code>AdditionTest</code> will be constructed with the default constructor
112     * and fields annotated by <code>&#064;Parameter</code>  will be initialized
113     * with the data values in the <code>&#064;Parameters</code> method.
114     *
115     * <p>
116     * The parameters can be provided as an array, too:
117     * 
118     * <pre>
119     * &#064;Parameters
120     * public static Object[][] data() {
121     *      return new Object[][] { { 0, 0, 0 }, { 1, 1, 2 }, { 3, 2, 5 }, { 4, 3, 7 } } };
122     * }
123     * </pre>
124     * 
125     * <h3>Tests with single parameter</h3>
126     * <p>
127     * If your test needs a single parameter only, you don't have to wrap it with an
128     * array. Instead you can provide an <code>Iterable</code> or an array of
129     * objects.
130     * <pre>
131     * &#064;Parameters
132     * public static Iterable&lt;? extends Object&gt; data() {
133     *      return Arrays.asList(&quot;first test&quot;, &quot;second test&quot;);
134     * }
135     * </pre>
136     * <p>
137     * or
138     * <pre>
139     * &#064;Parameters
140     * public static Object[] data() {
141     *      return new Object[] { &quot;first test&quot;, &quot;second test&quot; };
142     * }
143     * </pre>
144     *
145     * <h3>Executing code before/after executing tests for specific parameters</h3>
146     * <p>
147     * If your test needs to perform some preparation or cleanup based on the
148     * parameters, this can be done by adding public static methods annotated with
149     * {@code @BeforeParam}/{@code @AfterParam}. Such methods should either have no
150     * parameters or the same parameters as the test.
151     * <pre>
152     * &#064;BeforeParam
153     * public static void beforeTestsForParameter(String onlyParameter) {
154     *     System.out.println("Testing " + onlyParameter);
155     * }
156     * </pre>
157     *
158     * <h3>Create different runners</h3>
159     * <p>
160     * By default the {@code Parameterized} runner creates a slightly modified
161     * {@link BlockJUnit4ClassRunner} for each set of parameters. You can build an
162     * own {@code Parameterized} runner that creates another runner for each set of
163     * parameters. Therefore you have to build a {@link ParametersRunnerFactory}
164     * that creates a runner for each {@link TestWithParameters}. (
165     * {@code TestWithParameters} are bundling the parameters and the test name.)
166     * The factory must have a public zero-arg constructor.
167     *
168     * <pre>
169     * public class YourRunnerFactory implements ParametersRunnerFactory {
170     *     public Runner createRunnerForTestWithParameters(TestWithParameters test)
171     *             throws InitializationError {
172     *         return YourRunner(test);
173     *     }
174     * }
175     * </pre>
176     * <p>
177     * Use the {@link UseParametersRunnerFactory} to tell the {@code Parameterized}
178     * runner that it should use your factory.
179     *
180     * <pre>
181     * &#064;RunWith(Parameterized.class)
182     * &#064;UseParametersRunnerFactory(YourRunnerFactory.class)
183     * public class YourTest {
184     *     ...
185     * }
186     * </pre>
187     *
188     * <h3>Avoid creating parameters</h3>
189     * <p>With {@link org.junit.Assume assumptions} you can dynamically skip tests.
190     * Assumptions are also supported by the <code>&#064;Parameters</code> method.
191     * Creating parameters is stopped when the assumption fails and none of the
192     * tests in the test class is executed. JUnit reports a
193     * {@link Result#getAssumptionFailureCount() single assumption failure} for the
194     * whole test class in this case.
195     * <pre>
196     * &#064;Parameters
197     * public static Iterable&lt;? extends Object&gt; data() {
198     *      String os = System.getProperty("os.name").toLowerCase()
199     *      Assume.assumeTrue(os.contains("win"));
200     *      return Arrays.asList(&quot;first test&quot;, &quot;second test&quot;);
201     * }
202     * </pre>
203     * @since 4.0
204     */
205    public class Parameterized extends Suite {
206        /**
207         * Annotation for a method which provides parameters to be injected into the
208         * test class constructor by <code>Parameterized</code>. The method has to
209         * be public and static.
210         */
211        @Retention(RetentionPolicy.RUNTIME)
212        @Target(ElementType.METHOD)
213        public @interface Parameters {
214            /**
215             * Optional pattern to derive the test's name from the parameters. Use
216             * numbers in braces to refer to the parameters or the additional data
217             * as follows:
218             * <pre>
219             * {index} - the current parameter index
220             * {0} - the first parameter value
221             * {1} - the second parameter value
222             * etc...
223             * </pre>
224             * <p>
225             * Default value is "{index}" for compatibility with previous JUnit
226             * versions.
227             *
228             * @return {@link MessageFormat} pattern string, except the index
229             *         placeholder.
230             * @see MessageFormat
231             */
232            String name() default "{index}";
233        }
234    
235        /**
236         * Annotation for fields of the test class which will be initialized by the
237         * method annotated by <code>Parameters</code>.
238         * By using directly this annotation, the test class constructor isn't needed.
239         * Index range must start at 0.
240         * Default value is 0.
241         */
242        @Retention(RetentionPolicy.RUNTIME)
243        @Target(ElementType.FIELD)
244        public @interface Parameter {
245            /**
246             * Method that returns the index of the parameter in the array
247             * returned by the method annotated by <code>Parameters</code>.
248             * Index range must start at 0.
249             * Default value is 0.
250             *
251             * @return the index of the parameter.
252             */
253            int value() default 0;
254        }
255    
256        /**
257         * Add this annotation to your test class if you want to generate a special
258         * runner. You have to specify a {@link ParametersRunnerFactory} class that
259         * creates such runners. The factory must have a public zero-arg
260         * constructor.
261         */
262        @Retention(RetentionPolicy.RUNTIME)
263        @Inherited
264        @Target(ElementType.TYPE)
265        public @interface UseParametersRunnerFactory {
266            /**
267             * @return a {@link ParametersRunnerFactory} class (must have a default
268             *         constructor)
269             */
270            Class<? extends ParametersRunnerFactory> value() default BlockJUnit4ClassRunnerWithParametersFactory.class;
271        }
272    
273        /**
274         * Annotation for {@code public static void} methods which should be executed before
275         * evaluating tests with particular parameters.
276         *
277         * @see org.junit.BeforeClass
278         * @see org.junit.Before
279         * @since 4.13
280         */
281        @Retention(RetentionPolicy.RUNTIME)
282        @Target(ElementType.METHOD)
283        public @interface BeforeParam {
284        }
285    
286        /**
287         * Annotation for {@code public static void} methods which should be executed after
288         * evaluating tests with particular parameters.
289         *
290         * @see org.junit.AfterClass
291         * @see org.junit.After
292         * @since 4.13
293         */
294        @Retention(RetentionPolicy.RUNTIME)
295        @Target(ElementType.METHOD)
296        public @interface AfterParam {
297        }
298    
299        /**
300         * Only called reflectively. Do not use programmatically.
301         */
302        public Parameterized(Class<?> klass) throws Throwable {
303            this(klass, new RunnersFactory(klass));
304        }
305    
306        private Parameterized(Class<?> klass, RunnersFactory runnersFactory) throws Exception {
307            super(klass, runnersFactory.createRunners());
308            validateBeforeParamAndAfterParamMethods(runnersFactory.parameterCount);
309        }
310    
311        private void validateBeforeParamAndAfterParamMethods(Integer parameterCount)
312                throws InvalidTestClassError {
313            List<Throwable> errors = new ArrayList<Throwable>();
314            validatePublicStaticVoidMethods(Parameterized.BeforeParam.class, parameterCount, errors);
315            validatePublicStaticVoidMethods(Parameterized.AfterParam.class, parameterCount, errors);
316            if (!errors.isEmpty()) {
317                throw new InvalidTestClassError(getTestClass().getJavaClass(), errors);
318            }
319        }
320    
321        private void validatePublicStaticVoidMethods(
322                Class<? extends Annotation> annotation, Integer parameterCount,
323                List<Throwable> errors) {
324            List<FrameworkMethod> methods = getTestClass().getAnnotatedMethods(annotation);
325            for (FrameworkMethod fm : methods) {
326                fm.validatePublicVoid(true, errors);
327                if (parameterCount != null) {
328                    int methodParameterCount = fm.getMethod().getParameterTypes().length;
329                    if (methodParameterCount != 0 && methodParameterCount != parameterCount) {
330                        errors.add(new Exception("Method " + fm.getName()
331                                + "() should have 0 or " + parameterCount + " parameter(s)"));
332                    }
333                }
334            }
335        }
336    
337        private static class AssumptionViolationRunner extends Runner {
338            private final Description description;
339            private final AssumptionViolatedException exception;
340    
341            AssumptionViolationRunner(TestClass testClass, String methodName,
342                    AssumptionViolatedException exception) {
343                this.description = Description
344                        .createTestDescription(testClass.getJavaClass(),
345                                methodName + "() assumption violation");
346                this.exception = exception;
347            }
348    
349            @Override
350            public Description getDescription() {
351                return description;
352            }
353    
354            @Override
355            public void run(RunNotifier notifier) {
356                notifier.fireTestAssumptionFailed(new Failure(description, exception));
357            }
358        }
359    
360        private static class RunnersFactory {
361            private static final ParametersRunnerFactory DEFAULT_FACTORY = new BlockJUnit4ClassRunnerWithParametersFactory();
362    
363            private final TestClass testClass;
364            private final FrameworkMethod parametersMethod;
365            private final List<Object> allParameters;
366            private final int parameterCount;
367            private final Runner runnerOverride;
368    
369            private RunnersFactory(Class<?> klass) throws Throwable {
370                testClass = new TestClass(klass);
371                parametersMethod = getParametersMethod(testClass);
372                List<Object> allParametersResult;
373                AssumptionViolationRunner assumptionViolationRunner = null;
374                try {
375                    allParametersResult = allParameters(testClass, parametersMethod);
376                } catch (AssumptionViolatedException e) {
377                    allParametersResult = Collections.emptyList();
378                    assumptionViolationRunner = new AssumptionViolationRunner(testClass,
379                            parametersMethod.getName(), e);
380                }
381                allParameters = allParametersResult;
382                runnerOverride = assumptionViolationRunner;
383                parameterCount =
384                        allParameters.isEmpty() ? 0 : normalizeParameters(allParameters.get(0)).length;
385            }
386    
387            private List<Runner> createRunners() throws Exception {
388                if (runnerOverride != null) {
389                    return Collections.singletonList(runnerOverride);
390                }
391                Parameters parameters = parametersMethod.getAnnotation(Parameters.class);
392                return Collections.unmodifiableList(createRunnersForParameters(
393                        allParameters, parameters.name(),
394                        getParametersRunnerFactory()));
395            }
396    
397            private ParametersRunnerFactory getParametersRunnerFactory()
398                    throws InstantiationException, IllegalAccessException {
399                UseParametersRunnerFactory annotation = testClass
400                        .getAnnotation(UseParametersRunnerFactory.class);
401                if (annotation == null) {
402                    return DEFAULT_FACTORY;
403                } else {
404                    Class<? extends ParametersRunnerFactory> factoryClass = annotation
405                            .value();
406                    return factoryClass.newInstance();
407                }
408            }
409    
410            private TestWithParameters createTestWithNotNormalizedParameters(
411                    String pattern, int index, Object parametersOrSingleParameter) {
412                Object[] parameters = normalizeParameters(parametersOrSingleParameter);
413                return createTestWithParameters(testClass, pattern, index, parameters);
414            }
415    
416            private static Object[] normalizeParameters(Object parametersOrSingleParameter) {
417                return (parametersOrSingleParameter instanceof Object[]) ? (Object[]) parametersOrSingleParameter
418                        : new Object[] { parametersOrSingleParameter };
419            }
420    
421            @SuppressWarnings("unchecked")
422            private static List<Object> allParameters(
423                    TestClass testClass, FrameworkMethod parametersMethod) throws Throwable {
424                Object parameters = parametersMethod.invokeExplosively(null);
425                if (parameters instanceof List) {
426                    return (List<Object>) parameters;
427                } else if (parameters instanceof Collection) {
428                    return new ArrayList<Object>((Collection<Object>) parameters);
429                } else if (parameters instanceof Iterable) {
430                    List<Object> result = new ArrayList<Object>();
431                    for (Object entry : ((Iterable<Object>) parameters)) {
432                        result.add(entry);
433                    }
434                    return result;
435                } else if (parameters instanceof Object[]) {
436                    return Arrays.asList((Object[]) parameters);
437                } else {
438                    throw parametersMethodReturnedWrongType(testClass, parametersMethod);
439                }
440            }
441    
442            private static FrameworkMethod getParametersMethod(TestClass testClass) throws Exception {
443                List<FrameworkMethod> methods = testClass
444                        .getAnnotatedMethods(Parameters.class);
445                for (FrameworkMethod each : methods) {
446                    if (each.isStatic() && each.isPublic()) {
447                        return each;
448                    }
449                }
450    
451                throw new Exception("No public static parameters method on class "
452                        + testClass.getName());
453            }
454    
455            private List<Runner> createRunnersForParameters(
456                    Iterable<Object> allParameters, String namePattern,
457                    ParametersRunnerFactory runnerFactory) throws Exception {
458                try {
459                    List<TestWithParameters> tests = createTestsForParameters(
460                            allParameters, namePattern);
461                    List<Runner> runners = new ArrayList<Runner>();
462                    for (TestWithParameters test : tests) {
463                        runners.add(runnerFactory
464                                .createRunnerForTestWithParameters(test));
465                    }
466                    return runners;
467                } catch (ClassCastException e) {
468                    throw parametersMethodReturnedWrongType(testClass, parametersMethod);
469                }
470            }
471    
472            private List<TestWithParameters> createTestsForParameters(
473                    Iterable<Object> allParameters, String namePattern)
474                    throws Exception {
475                int i = 0;
476                List<TestWithParameters> children = new ArrayList<TestWithParameters>();
477                for (Object parametersOfSingleTest : allParameters) {
478                    children.add(createTestWithNotNormalizedParameters(namePattern,
479                            i++, parametersOfSingleTest));
480                }
481                return children;
482            }
483    
484            private static Exception parametersMethodReturnedWrongType(
485                    TestClass testClass, FrameworkMethod parametersMethod) throws Exception {
486                String className = testClass.getName();
487                String methodName = parametersMethod.getName();
488                String message = MessageFormat.format(
489                        "{0}.{1}() must return an Iterable of arrays.", className,
490                        methodName);
491                return new Exception(message);
492            }
493    
494            private TestWithParameters createTestWithParameters(
495                    TestClass testClass, String pattern, int index,
496                    Object[] parameters) {
497                String finalPattern = pattern.replaceAll("\\{index\\}",
498                        Integer.toString(index));
499                String name = MessageFormat.format(finalPattern, parameters);
500                return new TestWithParameters("[" + name + "]", testClass,
501                        Arrays.asList(parameters));
502            }
503        }
504    }