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