1 /** 2 * 3 */ 4 package org.junit.experimental.theories; 5 6 import java.lang.reflect.Field; 7 import java.lang.reflect.InvocationTargetException; 8 import java.lang.reflect.Modifier; 9 import java.util.ArrayList; 10 import java.util.List; 11 12 import org.junit.Assert; 13 import org.junit.experimental.theories.PotentialAssignment.CouldNotGenerateValueException; 14 import org.junit.experimental.theories.internal.Assignments; 15 import org.junit.experimental.theories.internal.ParameterizedAssertionError; 16 import org.junit.internal.AssumptionViolatedException; 17 import org.junit.runners.BlockJUnit4ClassRunner; 18 import org.junit.runners.model.FrameworkMethod; 19 import org.junit.runners.model.InitializationError; 20 import org.junit.runners.model.Statement; 21 import org.junit.runners.model.TestClass; 22 23 public class Theories extends BlockJUnit4ClassRunner { 24 public Theories(Class<?> klass) throws InitializationError { 25 super(klass); 26 } 27 28 @Override 29 protected void collectInitializationErrors(List<Throwable> errors) { 30 super.collectInitializationErrors(errors); 31 validateDataPointFields(errors); 32 } 33 34 private void validateDataPointFields(List<Throwable> errors) { 35 Field[] fields= getTestClass().getJavaClass().getDeclaredFields(); 36 37 for (Field each : fields) 38 if (each.getAnnotation(DataPoint.class) != null && !Modifier.isStatic(each.getModifiers())) 39 errors.add(new Error("DataPoint field " + each.getName() + " must be static")); 40 } 41 42 @Override 43 protected void validateConstructor(List<Throwable> errors) { 44 validateOnlyOneConstructor(errors); 45 } 46 47 @Override 48 protected void validateTestMethods(List<Throwable> errors) { 49 for (FrameworkMethod each : computeTestMethods()) 50 if(each.getAnnotation(Theory.class) != null) 51 each.validatePublicVoid(false, errors); 52 else 53 each.validatePublicVoidNoArg(false, errors); 54 } 55 56 @Override 57 protected List<FrameworkMethod> computeTestMethods() { 58 List<FrameworkMethod> testMethods= super.computeTestMethods(); 59 List<FrameworkMethod> theoryMethods= getTestClass().getAnnotatedMethods(Theory.class); 60 testMethods.removeAll(theoryMethods); 61 testMethods.addAll(theoryMethods); 62 return testMethods; 63 } 64 65 @Override 66 public Statement methodBlock(final FrameworkMethod method) { 67 return new TheoryAnchor(method, getTestClass()); 68 } 69 70 public static class TheoryAnchor extends Statement { 71 private int successes= 0; 72 73 private FrameworkMethod fTestMethod; 74 private TestClass fTestClass; 75 76 private List<AssumptionViolatedException> fInvalidParameters= new ArrayList<AssumptionViolatedException>(); 77 78 public TheoryAnchor(FrameworkMethod method, TestClass testClass) { 79 fTestMethod= method; 80 fTestClass= testClass; 81 } 82 83 private TestClass getTestClass() { 84 return fTestClass; 85 } 86 87 @Override 88 public void evaluate() throws Throwable { 89 runWithAssignment(Assignments.allUnassigned( 90 fTestMethod.getMethod(), getTestClass())); 91 92 if (successes == 0) 93 Assert 94 .fail("Never found parameters that satisfied method assumptions. Violated assumptions: " 95 + fInvalidParameters); 96 } 97 98 protected void runWithAssignment(Assignments parameterAssignment) 99 throws Throwable { 100 if (!parameterAssignment.isComplete()) { 101 runWithIncompleteAssignment(parameterAssignment); 102 } else { 103 runWithCompleteAssignment(parameterAssignment); 104 } 105 } 106 107 protected void runWithIncompleteAssignment(Assignments incomplete) 108 throws InstantiationException, IllegalAccessException, 109 Throwable { 110 for (PotentialAssignment source : incomplete 111 .potentialsForNextUnassigned()) { 112 runWithAssignment(incomplete.assignNext(source)); 113 } 114 } 115 116 protected void runWithCompleteAssignment(final Assignments complete) 117 throws InstantiationException, IllegalAccessException, 118 InvocationTargetException, NoSuchMethodException, Throwable { 119 new BlockJUnit4ClassRunner(getTestClass().getJavaClass()) { 120 @Override 121 protected void collectInitializationErrors( 122 List<Throwable> errors) { 123 // do nothing 124 } 125 126 @Override 127 public Statement methodBlock(FrameworkMethod method) { 128 final Statement statement= super.methodBlock(method); 129 return new Statement() { 130 @Override 131 public void evaluate() throws Throwable { 132 try { 133 statement.evaluate(); 134 handleDataPointSuccess(); 135 } catch (AssumptionViolatedException e) { 136 handleAssumptionViolation(e); 137 } catch (Throwable e) { 138 reportParameterizedError(e, complete 139 .getArgumentStrings(nullsOk())); 140 } 141 } 142 143 }; 144 } 145 146 @Override 147 protected Statement methodInvoker(FrameworkMethod method, Object test) { 148 return methodCompletesWithParameters(method, complete, test); 149 } 150 151 @Override 152 public Object createTest() throws Exception { 153 return getTestClass().getOnlyConstructor().newInstance( 154 complete.getConstructorArguments(nullsOk())); 155 } 156 }.methodBlock(fTestMethod).evaluate(); 157 } 158 159 private Statement methodCompletesWithParameters( 160 final FrameworkMethod method, final Assignments complete, final Object freshInstance) { 161 return new Statement() { 162 @Override 163 public void evaluate() throws Throwable { 164 try { 165 final Object[] values= complete.getMethodArguments( 166 nullsOk()); 167 method.invokeExplosively(freshInstance, values); 168 } catch (CouldNotGenerateValueException e) { 169 // ignore 170 } 171 } 172 }; 173 } 174 175 protected void handleAssumptionViolation(AssumptionViolatedException e) { 176 fInvalidParameters.add(e); 177 } 178 179 protected void reportParameterizedError(Throwable e, Object... params) 180 throws Throwable { 181 if (params.length == 0) 182 throw e; 183 throw new ParameterizedAssertionError(e, fTestMethod.getName(), 184 params); 185 } 186 187 private boolean nullsOk() { 188 Theory annotation= fTestMethod.getMethod().getAnnotation( 189 Theory.class); 190 if (annotation == null) 191 return false; 192 return annotation.nullsAccepted(); 193 } 194 195 protected void handleDataPointSuccess() { 196 successes++; 197 } 198 } 199 } 200