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 }