001package org.junit.runners;
002
003import java.lang.annotation.ElementType;
004import java.lang.annotation.Inherited;
005import java.lang.annotation.Retention;
006import java.lang.annotation.RetentionPolicy;
007import java.lang.annotation.Target;
008import java.text.MessageFormat;
009import java.util.ArrayList;
010import java.util.Arrays;
011import java.util.Collections;
012import java.util.List;
013
014import org.junit.runner.Runner;
015import org.junit.runners.model.FrameworkMethod;
016import org.junit.runners.model.InitializationError;
017import org.junit.runners.model.TestClass;
018import org.junit.runners.parameterized.BlockJUnit4ClassRunnerWithParametersFactory;
019import org.junit.runners.parameterized.ParametersRunnerFactory;
020import org.junit.runners.parameterized.TestWithParameters;
021
022/**
023 * The custom runner <code>Parameterized</code> implements parameterized tests.
024 * When running a parameterized test class, instances are created for the
025 * cross-product of the test methods and the test data elements.
026 * <p>
027 * For example, to test a Fibonacci function, write:
028 * <pre>
029 * &#064;RunWith(Parameterized.class)
030 * public class FibonacciTest {
031 *     &#064;Parameters(name= &quot;{index}: fib[{0}]={1}&quot;)
032 *     public static Iterable&lt;Object[]&gt; data() {
033 *         return Arrays.asList(new Object[][] { { 0, 0 }, { 1, 1 }, { 2, 1 },
034 *                 { 3, 2 }, { 4, 3 }, { 5, 5 }, { 6, 8 } });
035 *     }
036 *
037 *     private int fInput;
038 *
039 *     private int fExpected;
040 *
041 *     public FibonacciTest(int input, int expected) {
042 *         fInput= input;
043 *         fExpected= expected;
044 *     }
045 *
046 *     &#064;Test
047 *     public void test() {
048 *         assertEquals(fExpected, Fibonacci.compute(fInput));
049 *     }
050 * }
051 * </pre>
052 * <p>
053 * Each instance of <code>FibonacciTest</code> will be constructed using the
054 * two-argument constructor and the data values in the
055 * <code>&#064;Parameters</code> method.
056 * <p>
057 * In order that you can easily identify the individual tests, you may provide a
058 * name for the <code>&#064;Parameters</code> annotation. This name is allowed
059 * to contain placeholders, which are replaced at runtime. The placeholders are
060 * <dl>
061 * <dt>{index}</dt>
062 * <dd>the current parameter index</dd>
063 * <dt>{0}</dt>
064 * <dd>the first parameter value</dd>
065 * <dt>{1}</dt>
066 * <dd>the second parameter value</dd>
067 * <dt>...</dt>
068 * <dd>...</dd>
069 * </dl>
070 * <p>
071 * In the example given above, the <code>Parameterized</code> runner creates
072 * names like <code>[1: fib(3)=2]</code>. If you don't use the name parameter,
073 * then the current parameter index is used as name.
074 * <p>
075 * You can also write:
076 * <pre>
077 * &#064;RunWith(Parameterized.class)
078 * public class FibonacciTest {
079 *  &#064;Parameters
080 *  public static Iterable&lt;Object[]&gt; data() {
081 *      return Arrays.asList(new Object[][] { { 0, 0 }, { 1, 1 }, { 2, 1 },
082 *                 { 3, 2 }, { 4, 3 }, { 5, 5 }, { 6, 8 } });
083 *  }
084 *  
085 *  &#064;Parameter(0)
086 *  public int fInput;
087 *
088 *  &#064;Parameter(1)
089 *  public int fExpected;
090 *
091 *  &#064;Test
092 *  public void test() {
093 *      assertEquals(fExpected, Fibonacci.compute(fInput));
094 *  }
095 * }
096 * </pre>
097 * <p>
098 * Each instance of <code>FibonacciTest</code> will be constructed with the default constructor
099 * and fields annotated by <code>&#064;Parameter</code>  will be initialized
100 * with the data values in the <code>&#064;Parameters</code> method.
101 *
102 * <p>
103 * The parameters can be provided as an array, too:
104 * 
105 * <pre>
106 * &#064;Parameters
107 * public static Object[][] data() {
108 *      return new Object[][] { { 0, 0 }, { 1, 1 }, { 2, 1 }, { 3, 2 }, { 4, 3 },
109 *                      { 5, 5 }, { 6, 8 } };
110 * }
111 * </pre>
112 * 
113 * <h3>Tests with single parameter</h3>
114 * <p>
115 * If your test needs a single parameter only, you don't have to wrap it with an
116 * array. Instead you can provide an <code>Iterable</code> or an array of
117 * objects.
118 * <pre>
119 * &#064;Parameters
120 * public static Iterable&lt;? extends Object&gt; data() {
121 *      return Arrays.asList(&quot;first test&quot;, &quot;second test&quot;);
122 * }
123 * </pre>
124 * <p>
125 * or
126 * <pre>
127 * &#064;Parameters
128 * public static Object[] data() {
129 *      return new Object[] { &quot;first test&quot;, &quot;second test&quot; };
130 * }
131 * </pre>
132 *
133 * <h3>Create different runners</h3>
134 * <p>
135 * By default the {@code Parameterized} runner creates a slightly modified
136 * {@link BlockJUnit4ClassRunner} for each set of parameters. You can build an
137 * own {@code Parameterized} runner that creates another runner for each set of
138 * parameters. Therefore you have to build a {@link ParametersRunnerFactory}
139 * that creates a runner for each {@link TestWithParameters}. (
140 * {@code TestWithParameters} are bundling the parameters and the test name.)
141 * The factory must have a public zero-arg constructor.
142 *
143 * <pre>
144 * public class YourRunnerFactory implements ParameterizedRunnerFactory {
145 *     public Runner createRunnerForTestWithParameters(TestWithParameters test)
146 *             throws InitializationError {
147 *         return YourRunner(test);
148 *     }
149 * }
150 * </pre>
151 * <p>
152 * Use the {@link UseParametersRunnerFactory} to tell the {@code Parameterized}
153 * runner that it should use your factory.
154 *
155 * <pre>
156 * &#064;RunWith(Parameterized.class)
157 * &#064;UseParametersRunnerFactory(YourRunnerFactory.class)
158 * public class YourTest {
159 *     ...
160 * }
161 * </pre>
162 *
163 * @since 4.0
164 */
165public class Parameterized extends Suite {
166    /**
167     * Annotation for a method which provides parameters to be injected into the
168     * test class constructor by <code>Parameterized</code>. The method has to
169     * be public and static.
170     */
171    @Retention(RetentionPolicy.RUNTIME)
172    @Target(ElementType.METHOD)
173    public static @interface Parameters {
174        /**
175         * Optional pattern to derive the test's name from the parameters. Use
176         * numbers in braces to refer to the parameters or the additional data
177         * as follows:
178         * <pre>
179         * {index} - the current parameter index
180         * {0} - the first parameter value
181         * {1} - the second parameter value
182         * etc...
183         * </pre>
184         * <p>
185         * Default value is "{index}" for compatibility with previous JUnit
186         * versions.
187         *
188         * @return {@link MessageFormat} pattern string, except the index
189         *         placeholder.
190         * @see MessageFormat
191         */
192        String name() default "{index}";
193    }
194
195    /**
196     * Annotation for fields of the test class which will be initialized by the
197     * method annotated by <code>Parameters</code>.
198     * By using directly this annotation, the test class constructor isn't needed.
199     * Index range must start at 0.
200     * Default value is 0.
201     */
202    @Retention(RetentionPolicy.RUNTIME)
203    @Target(ElementType.FIELD)
204    public static @interface Parameter {
205        /**
206         * Method that returns the index of the parameter in the array
207         * returned by the method annotated by <code>Parameters</code>.
208         * Index range must start at 0.
209         * Default value is 0.
210         *
211         * @return the index of the parameter.
212         */
213        int value() default 0;
214    }
215
216    /**
217     * Add this annotation to your test class if you want to generate a special
218     * runner. You have to specify a {@link ParametersRunnerFactory} class that
219     * creates such runners. The factory must have a public zero-arg
220     * constructor.
221     */
222    @Retention(RetentionPolicy.RUNTIME)
223    @Inherited
224    @Target(ElementType.TYPE)
225    public @interface UseParametersRunnerFactory {
226        /**
227         * @return a {@link ParametersRunnerFactory} class (must have a default
228         *         constructor)
229         */
230        Class<? extends ParametersRunnerFactory> value() default BlockJUnit4ClassRunnerWithParametersFactory.class;
231    }
232
233    private static final ParametersRunnerFactory DEFAULT_FACTORY = new BlockJUnit4ClassRunnerWithParametersFactory();
234
235    private static final List<Runner> NO_RUNNERS = Collections.<Runner>emptyList();
236
237    private final List<Runner> runners;
238
239    /**
240     * Only called reflectively. Do not use programmatically.
241     */
242    public Parameterized(Class<?> klass) throws Throwable {
243        super(klass, NO_RUNNERS);
244        ParametersRunnerFactory runnerFactory = getParametersRunnerFactory(
245                klass);
246        Parameters parameters = getParametersMethod().getAnnotation(
247                Parameters.class);
248        runners = Collections.unmodifiableList(createRunnersForParameters(
249                allParameters(), parameters.name(), runnerFactory));
250    }
251
252    private ParametersRunnerFactory getParametersRunnerFactory(Class<?> klass)
253            throws InstantiationException, IllegalAccessException {
254        UseParametersRunnerFactory annotation = klass
255                .getAnnotation(UseParametersRunnerFactory.class);
256        if (annotation == null) {
257            return DEFAULT_FACTORY;
258        } else {
259            Class<? extends ParametersRunnerFactory> factoryClass = annotation
260                    .value();
261            return factoryClass.newInstance();
262        }
263    }
264
265    @Override
266    protected List<Runner> getChildren() {
267        return runners;
268    }
269
270    private TestWithParameters createTestWithNotNormalizedParameters(
271            String pattern, int index, Object parametersOrSingleParameter) {
272        Object[] parameters= (parametersOrSingleParameter instanceof Object[]) ? (Object[]) parametersOrSingleParameter
273            : new Object[] { parametersOrSingleParameter };
274        return createTestWithParameters(getTestClass(), pattern, index,
275                parameters);
276    }
277
278    @SuppressWarnings("unchecked")
279    private Iterable<Object> allParameters() throws Throwable {
280        Object parameters = getParametersMethod().invokeExplosively(null);
281        if (parameters instanceof Iterable) {
282            return (Iterable<Object>) parameters;
283        } else if (parameters instanceof Object[]) {
284            return Arrays.asList((Object[]) parameters);
285        } else {
286            throw parametersMethodReturnedWrongType();
287        }
288    }
289
290    private FrameworkMethod getParametersMethod() throws Exception {
291        List<FrameworkMethod> methods = getTestClass().getAnnotatedMethods(
292                Parameters.class);
293        for (FrameworkMethod each : methods) {
294            if (each.isStatic() && each.isPublic()) {
295                return each;
296            }
297        }
298
299        throw new Exception("No public static parameters method on class "
300                + getTestClass().getName());
301    }
302
303    private List<Runner> createRunnersForParameters(
304            Iterable<Object> allParameters, String namePattern,
305            ParametersRunnerFactory runnerFactory)
306            throws InitializationError,
307            Exception {
308        try {
309            List<TestWithParameters> tests = createTestsForParameters(
310                    allParameters, namePattern);
311            List<Runner> runners = new ArrayList<Runner>();
312            for (TestWithParameters test : tests) {
313                runners.add(runnerFactory
314                        .createRunnerForTestWithParameters(test));
315            }
316            return runners;
317        } catch (ClassCastException e) {
318            throw parametersMethodReturnedWrongType();
319        }
320    }
321
322    private List<TestWithParameters> createTestsForParameters(
323            Iterable<Object> allParameters, String namePattern)
324            throws Exception {
325        int i = 0;
326        List<TestWithParameters> children = new ArrayList<TestWithParameters>();
327        for (Object parametersOfSingleTest : allParameters) {
328            children.add(createTestWithNotNormalizedParameters(namePattern,
329                    i++, parametersOfSingleTest));
330        }
331        return children;
332    }
333
334    private Exception parametersMethodReturnedWrongType() throws Exception {
335        String className = getTestClass().getName();
336        String methodName = getParametersMethod().getName();
337        String message = MessageFormat.format(
338                "{0}.{1}() must return an Iterable of arrays.",
339                className, methodName);
340        return new Exception(message);
341    }
342
343    private static TestWithParameters createTestWithParameters(
344            TestClass testClass, String pattern, int index, Object[] parameters) {
345        String finalPattern = pattern.replaceAll("\\{index\\}",
346                Integer.toString(index));
347        String name = MessageFormat.format(finalPattern, parameters);
348        return new TestWithParameters("[" + name + "]", testClass,
349                Arrays.asList(parameters));
350    }
351}