001 package org.junit.runners.parameterized;
002
003 import java.lang.annotation.Annotation;
004 import java.lang.reflect.Field;
005 import java.util.List;
006
007 import org.junit.runner.notification.RunNotifier;
008 import org.junit.runners.BlockJUnit4ClassRunner;
009 import org.junit.runners.Parameterized.Parameter;
010 import org.junit.runners.model.FrameworkField;
011 import org.junit.runners.model.FrameworkMethod;
012 import org.junit.runners.model.InitializationError;
013 import 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 */
019 public 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 }