001 package org.junit.runners.model;
002
003 import static java.lang.reflect.Modifier.isStatic;
004 import static org.junit.internal.MethodSorter.NAME_ASCENDING;
005
006 import java.lang.annotation.Annotation;
007 import java.lang.reflect.Constructor;
008 import java.lang.reflect.Field;
009 import java.lang.reflect.Method;
010 import java.lang.reflect.Modifier;
011 import java.util.ArrayList;
012 import java.util.Arrays;
013 import java.util.Collections;
014 import java.util.Comparator;
015 import java.util.LinkedHashMap;
016 import java.util.LinkedHashSet;
017 import java.util.List;
018 import java.util.Map;
019 import java.util.Set;
020
021 import org.junit.Assert;
022 import org.junit.Before;
023 import org.junit.BeforeClass;
024 import org.junit.internal.MethodSorter;
025
026 /**
027 * Wraps a class to be run, providing method validation and annotation searching
028 *
029 * @since 4.5
030 */
031 public class TestClass implements Annotatable {
032 private static final FieldComparator FIELD_COMPARATOR = new FieldComparator();
033 private static final MethodComparator METHOD_COMPARATOR = new MethodComparator();
034
035 private final Class<?> clazz;
036 private final Map<Class<? extends Annotation>, List<FrameworkMethod>> methodsForAnnotations;
037 private final Map<Class<? extends Annotation>, List<FrameworkField>> fieldsForAnnotations;
038
039 /**
040 * Creates a {@code TestClass} wrapping {@code clazz}. Each time this
041 * constructor executes, the class is scanned for annotations, which can be
042 * an expensive process (we hope in future JDK's it will not be.) Therefore,
043 * try to share instances of {@code TestClass} where possible.
044 */
045 public TestClass(Class<?> clazz) {
046 this.clazz = clazz;
047 if (clazz != null && clazz.getConstructors().length > 1) {
048 throw new IllegalArgumentException(
049 "Test class can only have one constructor");
050 }
051
052 Map<Class<? extends Annotation>, List<FrameworkMethod>> methodsForAnnotations =
053 new LinkedHashMap<Class<? extends Annotation>, List<FrameworkMethod>>();
054 Map<Class<? extends Annotation>, List<FrameworkField>> fieldsForAnnotations =
055 new LinkedHashMap<Class<? extends Annotation>, List<FrameworkField>>();
056
057 scanAnnotatedMembers(methodsForAnnotations, fieldsForAnnotations);
058
059 this.methodsForAnnotations = makeDeeplyUnmodifiable(methodsForAnnotations);
060 this.fieldsForAnnotations = makeDeeplyUnmodifiable(fieldsForAnnotations);
061 }
062
063 protected void scanAnnotatedMembers(Map<Class<? extends Annotation>, List<FrameworkMethod>> methodsForAnnotations, Map<Class<? extends Annotation>, List<FrameworkField>> fieldsForAnnotations) {
064 for (Class<?> eachClass : getSuperClasses(clazz)) {
065 for (Method eachMethod : MethodSorter.getDeclaredMethods(eachClass)) {
066 addToAnnotationLists(new FrameworkMethod(eachMethod), methodsForAnnotations);
067 }
068 // ensuring fields are sorted to make sure that entries are inserted
069 // and read from fieldForAnnotations in a deterministic order
070 for (Field eachField : getSortedDeclaredFields(eachClass)) {
071 addToAnnotationLists(new FrameworkField(eachField), fieldsForAnnotations);
072 }
073 }
074 }
075
076 private static Field[] getSortedDeclaredFields(Class<?> clazz) {
077 Field[] declaredFields = clazz.getDeclaredFields();
078 Arrays.sort(declaredFields, FIELD_COMPARATOR);
079 return declaredFields;
080 }
081
082 protected static <T extends FrameworkMember<T>> void addToAnnotationLists(T member,
083 Map<Class<? extends Annotation>, List<T>> map) {
084 for (Annotation each : member.getAnnotations()) {
085 Class<? extends Annotation> type = each.annotationType();
086 List<T> members = getAnnotatedMembers(map, type, true);
087 T memberToAdd = member.handlePossibleBridgeMethod(members);
088 if (memberToAdd == null) {
089 return;
090 }
091 if (runsTopToBottom(type)) {
092 members.add(0, memberToAdd);
093 } else {
094 members.add(memberToAdd);
095 }
096 }
097 }
098
099 private static <T extends FrameworkMember<T>> Map<Class<? extends Annotation>, List<T>>
100 makeDeeplyUnmodifiable(Map<Class<? extends Annotation>, List<T>> source) {
101 Map<Class<? extends Annotation>, List<T>> copy =
102 new LinkedHashMap<Class<? extends Annotation>, List<T>>();
103 for (Map.Entry<Class<? extends Annotation>, List<T>> entry : source.entrySet()) {
104 copy.put(entry.getKey(), Collections.unmodifiableList(entry.getValue()));
105 }
106 return Collections.unmodifiableMap(copy);
107 }
108
109 /**
110 * Returns, efficiently, all the non-overridden methods in this class and
111 * its superclasses that are annotated}.
112 *
113 * @since 4.12
114 */
115 public List<FrameworkMethod> getAnnotatedMethods() {
116 List<FrameworkMethod> methods = collectValues(methodsForAnnotations);
117 Collections.sort(methods, METHOD_COMPARATOR);
118 return methods;
119 }
120
121 /**
122 * Returns, efficiently, all the non-overridden methods in this class and
123 * its superclasses that are annotated with {@code annotationClass}.
124 */
125 public List<FrameworkMethod> getAnnotatedMethods(
126 Class<? extends Annotation> annotationClass) {
127 return Collections.unmodifiableList(getAnnotatedMembers(methodsForAnnotations, annotationClass, false));
128 }
129
130 /**
131 * Returns, efficiently, all the non-overridden fields in this class and its
132 * superclasses that are annotated.
133 *
134 * @since 4.12
135 */
136 public List<FrameworkField> getAnnotatedFields() {
137 return collectValues(fieldsForAnnotations);
138 }
139
140 /**
141 * Returns, efficiently, all the non-overridden fields in this class and its
142 * superclasses that are annotated with {@code annotationClass}.
143 */
144 public List<FrameworkField> getAnnotatedFields(
145 Class<? extends Annotation> annotationClass) {
146 return Collections.unmodifiableList(getAnnotatedMembers(fieldsForAnnotations, annotationClass, false));
147 }
148
149 private <T> List<T> collectValues(Map<?, List<T>> map) {
150 Set<T> values = new LinkedHashSet<T>();
151 for (List<T> additionalValues : map.values()) {
152 values.addAll(additionalValues);
153 }
154 return new ArrayList<T>(values);
155 }
156
157 private static <T> List<T> getAnnotatedMembers(Map<Class<? extends Annotation>, List<T>> map,
158 Class<? extends Annotation> type, boolean fillIfAbsent) {
159 if (!map.containsKey(type) && fillIfAbsent) {
160 map.put(type, new ArrayList<T>());
161 }
162 List<T> members = map.get(type);
163 return members == null ? Collections.<T>emptyList() : members;
164 }
165
166 private static boolean runsTopToBottom(Class<? extends Annotation> annotation) {
167 return annotation.equals(Before.class)
168 || annotation.equals(BeforeClass.class);
169 }
170
171 private static List<Class<?>> getSuperClasses(Class<?> testClass) {
172 List<Class<?>> results = new ArrayList<Class<?>>();
173 Class<?> current = testClass;
174 while (current != null) {
175 results.add(current);
176 current = current.getSuperclass();
177 }
178 return results;
179 }
180
181 /**
182 * Returns the underlying Java class.
183 */
184 public Class<?> getJavaClass() {
185 return clazz;
186 }
187
188 /**
189 * Returns the class's name.
190 */
191 public String getName() {
192 if (clazz == null) {
193 return "null";
194 }
195 return clazz.getName();
196 }
197
198 /**
199 * Returns the only public constructor in the class, or throws an {@code
200 * AssertionError} if there are more or less than one.
201 */
202
203 public Constructor<?> getOnlyConstructor() {
204 Constructor<?>[] constructors = clazz.getConstructors();
205 Assert.assertEquals(1, constructors.length);
206 return constructors[0];
207 }
208
209 /**
210 * Returns the annotations on this class
211 */
212 public Annotation[] getAnnotations() {
213 if (clazz == null) {
214 return new Annotation[0];
215 }
216 return clazz.getAnnotations();
217 }
218
219 public <T extends Annotation> T getAnnotation(Class<T> annotationType) {
220 if (clazz == null) {
221 return null;
222 }
223 return clazz.getAnnotation(annotationType);
224 }
225
226 public <T> List<T> getAnnotatedFieldValues(Object test,
227 Class<? extends Annotation> annotationClass, Class<T> valueClass) {
228 final List<T> results = new ArrayList<T>();
229 collectAnnotatedFieldValues(test, annotationClass, valueClass,
230 new MemberValueConsumer<T>() {
231 public void accept(FrameworkMember<?> member, T value) {
232 results.add(value);
233 }
234 });
235 return results;
236 }
237
238 /**
239 * Finds the fields annotated with the specified annotation and having the specified type,
240 * retrieves the values and passes those to the specified consumer.
241 *
242 * @since 4.13
243 */
244 public <T> void collectAnnotatedFieldValues(Object test,
245 Class<? extends Annotation> annotationClass, Class<T> valueClass,
246 MemberValueConsumer<T> consumer) {
247 for (FrameworkField each : getAnnotatedFields(annotationClass)) {
248 try {
249 Object fieldValue = each.get(test);
250 if (valueClass.isInstance(fieldValue)) {
251 consumer.accept(each, valueClass.cast(fieldValue));
252 }
253 } catch (IllegalAccessException e) {
254 throw new RuntimeException(
255 "How did getFields return a field we couldn't access?", e);
256 }
257 }
258 }
259
260 public <T> List<T> getAnnotatedMethodValues(Object test,
261 Class<? extends Annotation> annotationClass, Class<T> valueClass) {
262 final List<T> results = new ArrayList<T>();
263 collectAnnotatedMethodValues(test, annotationClass, valueClass,
264 new MemberValueConsumer<T>() {
265 public void accept(FrameworkMember<?> member, T value) {
266 results.add(value);
267 }
268 });
269 return results;
270 }
271
272 /**
273 * Finds the methods annotated with the specified annotation and returning the specified type,
274 * invokes it and pass the return value to the specified consumer.
275 *
276 * @since 4.13
277 */
278 public <T> void collectAnnotatedMethodValues(Object test,
279 Class<? extends Annotation> annotationClass, Class<T> valueClass,
280 MemberValueConsumer<T> consumer) {
281 for (FrameworkMethod each : getAnnotatedMethods(annotationClass)) {
282 try {
283 /*
284 * A method annotated with @Rule may return a @TestRule or a @MethodRule,
285 * we cannot call the method to check whether the return type matches our
286 * expectation i.e. subclass of valueClass. If we do that then the method
287 * will be invoked twice and we do not want to do that. So we first check
288 * whether return type matches our expectation and only then call the method
289 * to fetch the MethodRule
290 */
291 if (valueClass.isAssignableFrom(each.getReturnType())) {
292 Object fieldValue = each.invokeExplosively(test);
293 consumer.accept(each, valueClass.cast(fieldValue));
294 }
295 } catch (Throwable e) {
296 throw new RuntimeException(
297 "Exception in " + each.getName(), e);
298 }
299 }
300 }
301
302 public boolean isPublic() {
303 return Modifier.isPublic(clazz.getModifiers());
304 }
305
306 public boolean isANonStaticInnerClass() {
307 return clazz.isMemberClass() && !isStatic(clazz.getModifiers());
308 }
309
310 @Override
311 public int hashCode() {
312 return (clazz == null) ? 0 : clazz.hashCode();
313 }
314
315 @Override
316 public boolean equals(Object obj) {
317 if (this == obj) {
318 return true;
319 }
320 if (obj == null) {
321 return false;
322 }
323 if (getClass() != obj.getClass()) {
324 return false;
325 }
326 TestClass other = (TestClass) obj;
327 return clazz == other.clazz;
328 }
329
330 /**
331 * Compares two fields by its name.
332 */
333 private static class FieldComparator implements Comparator<Field> {
334 public int compare(Field left, Field right) {
335 return left.getName().compareTo(right.getName());
336 }
337 }
338
339 /**
340 * Compares two methods by its name.
341 */
342 private static class MethodComparator implements
343 Comparator<FrameworkMethod> {
344 public int compare(FrameworkMethod left, FrameworkMethod right) {
345 return NAME_ASCENDING.compare(left.getMethod(), right.getMethod());
346 }
347 }
348 }