001package org.junit.runners.parameterized;
002
003import java.lang.annotation.Annotation;
004import java.lang.reflect.Field;
005import java.util.List;
006
007import org.junit.runner.notification.RunNotifier;
008import org.junit.runners.BlockJUnit4ClassRunner;
009import org.junit.runners.Parameterized.Parameter;
010import org.junit.runners.model.FrameworkField;
011import org.junit.runners.model.FrameworkMethod;
012import org.junit.runners.model.InitializationError;
013import org.junit.runners.model.Statement;
014
015/**
016 * A {@link BlockJUnit4ClassRunner} with parameters support. Parameters can be
017 * injected via constructor or into annotated fields.
018 */
019public class BlockJUnit4ClassRunnerWithParameters extends
020        BlockJUnit4ClassRunner {
021    private final Object[] parameters;
022
023    private final String name;
024
025    public BlockJUnit4ClassRunnerWithParameters(TestWithParameters test)
026            throws InitializationError {
027        super(test.getTestClass().getJavaClass());
028        parameters = test.getParameters().toArray(
029                new Object[test.getParameters().size()]);
030        name = test.getName();
031    }
032
033    @Override
034    public Object createTest() throws Exception {
035        if (fieldsAreAnnotated()) {
036            return createTestUsingFieldInjection();
037        } else {
038            return createTestUsingConstructorInjection();
039        }
040    }
041
042    private Object createTestUsingConstructorInjection() throws Exception {
043        return getTestClass().getOnlyConstructor().newInstance(parameters);
044    }
045
046    private Object createTestUsingFieldInjection() throws Exception {
047        List<FrameworkField> annotatedFieldsByParameter = getAnnotatedFieldsByParameter();
048        if (annotatedFieldsByParameter.size() != parameters.length) {
049            throw new Exception(
050                    "Wrong number of parameters and @Parameter fields."
051                            + " @Parameter fields counted: "
052                            + annotatedFieldsByParameter.size()
053                            + ", available parameters: " + parameters.length
054                            + ".");
055        }
056        Object testClassInstance = getTestClass().getJavaClass().newInstance();
057        for (FrameworkField each : annotatedFieldsByParameter) {
058            Field field = each.getField();
059            Parameter annotation = field.getAnnotation(Parameter.class);
060            int index = annotation.value();
061            try {
062                field.set(testClassInstance, parameters[index]);
063            } catch (IllegalArgumentException iare) {
064                throw new Exception(getTestClass().getName()
065                        + ": Trying to set " + field.getName()
066                        + " with the value " + parameters[index]
067                        + " that is not the right type ("
068                        + parameters[index].getClass().getSimpleName()
069                        + " instead of " + field.getType().getSimpleName()
070                        + ").", iare);
071            }
072        }
073        return testClassInstance;
074    }
075
076    @Override
077    protected String getName() {
078        return name;
079    }
080
081    @Override
082    protected String testName(FrameworkMethod method) {
083        return method.getName() + getName();
084    }
085
086    @Override
087    protected void validateConstructor(List<Throwable> errors) {
088        validateOnlyOneConstructor(errors);
089        if (fieldsAreAnnotated()) {
090            validateZeroArgConstructor(errors);
091        }
092    }
093
094    @Override
095    protected void validateFields(List<Throwable> errors) {
096        super.validateFields(errors);
097        if (fieldsAreAnnotated()) {
098            List<FrameworkField> annotatedFieldsByParameter = getAnnotatedFieldsByParameter();
099            int[] usedIndices = new int[annotatedFieldsByParameter.size()];
100            for (FrameworkField each : annotatedFieldsByParameter) {
101                int index = each.getField().getAnnotation(Parameter.class)
102                        .value();
103                if (index < 0 || index > annotatedFieldsByParameter.size() - 1) {
104                    errors.add(new Exception("Invalid @Parameter value: "
105                            + index + ". @Parameter fields counted: "
106                            + annotatedFieldsByParameter.size()
107                            + ". Please use an index between 0 and "
108                            + (annotatedFieldsByParameter.size() - 1) + "."));
109                } else {
110                    usedIndices[index]++;
111                }
112            }
113            for (int index = 0; index < usedIndices.length; index++) {
114                int numberOfUse = usedIndices[index];
115                if (numberOfUse == 0) {
116                    errors.add(new Exception("@Parameter(" + index
117                            + ") is never used."));
118                } else if (numberOfUse > 1) {
119                    errors.add(new Exception("@Parameter(" + index
120                            + ") is used more than once (" + numberOfUse + ")."));
121                }
122            }
123        }
124    }
125
126    @Override
127    protected Statement classBlock(RunNotifier notifier) {
128        return childrenInvoker(notifier);
129    }
130
131    @Override
132    protected Annotation[] getRunnerAnnotations() {
133        return new Annotation[0];
134    }
135
136    private List<FrameworkField> getAnnotatedFieldsByParameter() {
137        return getTestClass().getAnnotatedFields(Parameter.class);
138    }
139
140    private boolean fieldsAreAnnotated() {
141        return !getAnnotatedFieldsByParameter().isEmpty();
142    }
143}