1 package org.junit.internal.runners.rules; 2 3 import org.junit.ClassRule; 4 import org.junit.Rule; 5 import org.junit.rules.MethodRule; 6 import org.junit.rules.TestRule; 7 import org.junit.runners.model.FrameworkMember; 8 import org.junit.runners.model.TestClass; 9 10 import java.lang.annotation.Annotation; 11 import java.lang.reflect.Modifier; 12 import java.util.ArrayList; 13 import java.util.List; 14 15 /** 16 * A RuleMemberValidator validates the rule fields/methods of a 17 * {@link org.junit.runners.model.TestClass}. All reasons for rejecting the 18 * {@code TestClass} are written to a list of errors. 19 * 20 * <p>There are four slightly different validators. The {@link #CLASS_RULE_VALIDATOR} 21 * validates fields with a {@link ClassRule} annotation and the 22 * {@link #RULE_VALIDATOR} validates fields with a {@link Rule} annotation.</p> 23 * 24 * <p>The {@link #CLASS_RULE_METHOD_VALIDATOR} 25 * validates methods with a {@link ClassRule} annotation and the 26 * {@link #RULE_METHOD_VALIDATOR} validates methods with a {@link Rule} annotation.</p> 27 */ 28 public class RuleMemberValidator { 29 /** 30 * Validates fields with a {@link ClassRule} annotation. 31 */ 32 public static final RuleMemberValidator CLASS_RULE_VALIDATOR = 33 classRuleValidatorBuilder() 34 .withValidator(new DeclaringClassMustBePublic()) 35 .withValidator(new MemberMustBeStatic()) 36 .withValidator(new MemberMustBePublic()) 37 .withValidator(new FieldMustBeATestRule()) 38 .build(); 39 /** 40 * Validates fields with a {@link Rule} annotation. 41 */ 42 public static final RuleMemberValidator RULE_VALIDATOR = 43 testRuleValidatorBuilder() 44 .withValidator(new MemberMustBeNonStaticOrAlsoClassRule()) 45 .withValidator(new MemberMustBePublic()) 46 .withValidator(new FieldMustBeARule()) 47 .build(); 48 /** 49 * Validates methods with a {@link ClassRule} annotation. 50 */ 51 public static final RuleMemberValidator CLASS_RULE_METHOD_VALIDATOR = 52 classRuleValidatorBuilder() 53 .forMethods() 54 .withValidator(new DeclaringClassMustBePublic()) 55 .withValidator(new MemberMustBeStatic()) 56 .withValidator(new MemberMustBePublic()) 57 .withValidator(new MethodMustBeATestRule()) 58 .build(); 59 60 /** 61 * Validates methods with a {@link Rule} annotation. 62 */ 63 public static final RuleMemberValidator RULE_METHOD_VALIDATOR = 64 testRuleValidatorBuilder() 65 .forMethods() 66 .withValidator(new MemberMustBeNonStaticOrAlsoClassRule()) 67 .withValidator(new MemberMustBePublic()) 68 .withValidator(new MethodMustBeARule()) 69 .build(); 70 71 private final Class<? extends Annotation> annotation; 72 private final boolean methods; 73 private final List<RuleValidator> validatorStrategies; 74 RuleMemberValidator(Builder builder)75 RuleMemberValidator(Builder builder) { 76 this.annotation = builder.annotation; 77 this.methods = builder.methods; 78 this.validatorStrategies = builder.validators; 79 } 80 81 /** 82 * Validate the {@link org.junit.runners.model.TestClass} and adds reasons 83 * for rejecting the class to a list of errors. 84 * 85 * @param target the {@code TestClass} to validate. 86 * @param errors the list of errors. 87 */ validate(TestClass target, List<Throwable> errors)88 public void validate(TestClass target, List<Throwable> errors) { 89 List<? extends FrameworkMember<?>> members = methods ? target.getAnnotatedMethods(annotation) 90 : target.getAnnotatedFields(annotation); 91 92 for (FrameworkMember<?> each : members) { 93 validateMember(each, errors); 94 } 95 } 96 validateMember(FrameworkMember<?> member, List<Throwable> errors)97 private void validateMember(FrameworkMember<?> member, List<Throwable> errors) { 98 for (RuleValidator strategy : validatorStrategies) { 99 strategy.validate(member, annotation, errors); 100 } 101 } 102 classRuleValidatorBuilder()103 private static Builder classRuleValidatorBuilder() { 104 return new Builder(ClassRule.class); 105 } 106 testRuleValidatorBuilder()107 private static Builder testRuleValidatorBuilder() { 108 return new Builder(Rule.class); 109 } 110 111 private static class Builder { 112 private final Class<? extends Annotation> annotation; 113 private boolean methods; 114 private final List<RuleValidator> validators; 115 Builder(Class<? extends Annotation> annotation)116 private Builder(Class<? extends Annotation> annotation) { 117 this.annotation = annotation; 118 this.methods = false; 119 this.validators = new ArrayList<RuleValidator>(); 120 } 121 forMethods()122 Builder forMethods() { 123 methods = true; 124 return this; 125 } 126 withValidator(RuleValidator validator)127 Builder withValidator(RuleValidator validator) { 128 validators.add(validator); 129 return this; 130 } 131 build()132 RuleMemberValidator build() { 133 return new RuleMemberValidator(this); 134 } 135 } 136 isRuleType(FrameworkMember<?> member)137 private static boolean isRuleType(FrameworkMember<?> member) { 138 return isMethodRule(member) || isTestRule(member); 139 } 140 isTestRule(FrameworkMember<?> member)141 private static boolean isTestRule(FrameworkMember<?> member) { 142 return TestRule.class.isAssignableFrom(member.getType()); 143 } 144 isMethodRule(FrameworkMember<?> member)145 private static boolean isMethodRule(FrameworkMember<?> member) { 146 return MethodRule.class.isAssignableFrom(member.getType()); 147 } 148 149 /** 150 * Encapsulates a single piece of validation logic, used to determine if {@link org.junit.Rule} and 151 * {@link org.junit.ClassRule} annotations have been used correctly 152 */ 153 interface RuleValidator { 154 /** 155 * Examine the given member and add any violations of the strategy's validation logic to the given list of errors 156 * @param member The member (field or member) to examine 157 * @param annotation The type of rule annotation on the member 158 * @param errors The list of errors to add validation violations to 159 */ validate(FrameworkMember<?> member, Class<? extends Annotation> annotation, List<Throwable> errors)160 void validate(FrameworkMember<?> member, Class<? extends Annotation> annotation, List<Throwable> errors); 161 } 162 163 /** 164 * Requires the validated member to be non-static 165 */ 166 private static final class MemberMustBeNonStaticOrAlsoClassRule implements RuleValidator { validate(FrameworkMember<?> member, Class<? extends Annotation> annotation, List<Throwable> errors)167 public void validate(FrameworkMember<?> member, Class<? extends Annotation> annotation, List<Throwable> errors) { 168 boolean isMethodRuleMember = isMethodRule(member); 169 boolean isClassRuleAnnotated = (member.getAnnotation(ClassRule.class) != null); 170 171 // We disallow: 172 // - static MethodRule members 173 // - static @Rule annotated members 174 // - UNLESS they're also @ClassRule annotated 175 // Note that MethodRule cannot be annotated with @ClassRule 176 if (member.isStatic() && (isMethodRuleMember || !isClassRuleAnnotated)) { 177 String message; 178 if (isMethodRule(member)) { 179 message = "must not be static."; 180 } else { 181 message = "must not be static or it must be annotated with @ClassRule."; 182 } 183 errors.add(new ValidationError(member, annotation, message)); 184 } 185 } 186 } 187 188 /** 189 * Requires the member to be static 190 */ 191 private static final class MemberMustBeStatic implements RuleValidator { validate(FrameworkMember<?> member, Class<? extends Annotation> annotation, List<Throwable> errors)192 public void validate(FrameworkMember<?> member, Class<? extends Annotation> annotation, List<Throwable> errors) { 193 if (!member.isStatic()) { 194 errors.add(new ValidationError(member, annotation, 195 "must be static.")); 196 } 197 } 198 } 199 200 /** 201 * Requires the member's declaring class to be public 202 */ 203 private static final class DeclaringClassMustBePublic implements RuleValidator { validate(FrameworkMember<?> member, Class<? extends Annotation> annotation, List<Throwable> errors)204 public void validate(FrameworkMember<?> member, Class<? extends Annotation> annotation, List<Throwable> errors) { 205 if (!isDeclaringClassPublic(member)) { 206 errors.add(new ValidationError(member, annotation, 207 "must be declared in a public class.")); 208 } 209 } 210 isDeclaringClassPublic(FrameworkMember<?> member)211 private boolean isDeclaringClassPublic(FrameworkMember<?> member) { 212 return Modifier.isPublic(member.getDeclaringClass().getModifiers()); 213 } 214 } 215 216 /** 217 * Requires the member to be public 218 */ 219 private static final class MemberMustBePublic implements RuleValidator { validate(FrameworkMember<?> member, Class<? extends Annotation> annotation, List<Throwable> errors)220 public void validate(FrameworkMember<?> member, Class<? extends Annotation> annotation, List<Throwable> errors) { 221 if (!member.isPublic()) { 222 errors.add(new ValidationError(member, annotation, 223 "must be public.")); 224 } 225 } 226 } 227 228 /** 229 * Requires the member is a field implementing {@link org.junit.rules.MethodRule} or {@link org.junit.rules.TestRule} 230 */ 231 private static final class FieldMustBeARule implements RuleValidator { validate(FrameworkMember<?> member, Class<? extends Annotation> annotation, List<Throwable> errors)232 public void validate(FrameworkMember<?> member, Class<? extends Annotation> annotation, List<Throwable> errors) { 233 if (!isRuleType(member)) { 234 errors.add(new ValidationError(member, annotation, 235 "must implement MethodRule or TestRule.")); 236 } 237 } 238 } 239 240 /** 241 * Require the member to return an implementation of {@link org.junit.rules.MethodRule} or 242 * {@link org.junit.rules.TestRule} 243 */ 244 private static final class MethodMustBeARule implements RuleValidator { validate(FrameworkMember<?> member, Class<? extends Annotation> annotation, List<Throwable> errors)245 public void validate(FrameworkMember<?> member, Class<? extends Annotation> annotation, List<Throwable> errors) { 246 if (!isRuleType(member)) { 247 errors.add(new ValidationError(member, annotation, 248 "must return an implementation of MethodRule or TestRule.")); 249 } 250 } 251 } 252 253 /** 254 * Require the member to return an implementation of {@link org.junit.rules.TestRule} 255 */ 256 private static final class MethodMustBeATestRule implements RuleValidator { validate(FrameworkMember<?> member, Class<? extends Annotation> annotation, List<Throwable> errors)257 public void validate(FrameworkMember<?> member, 258 Class<? extends Annotation> annotation, List<Throwable> errors) { 259 if (!isTestRule(member)) { 260 errors.add(new ValidationError(member, annotation, 261 "must return an implementation of TestRule.")); 262 } 263 } 264 } 265 266 /** 267 * Requires the member is a field implementing {@link org.junit.rules.TestRule} 268 */ 269 private static final class FieldMustBeATestRule implements RuleValidator { 270 validate(FrameworkMember<?> member, Class<? extends Annotation> annotation, List<Throwable> errors)271 public void validate(FrameworkMember<?> member, 272 Class<? extends Annotation> annotation, List<Throwable> errors) { 273 if (!isTestRule(member)) { 274 errors.add(new ValidationError(member, annotation, 275 "must implement TestRule.")); 276 } 277 } 278 } 279 } 280