• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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