1 package org.junit.runners.model; 2 3 import static java.lang.reflect.Modifier.isStatic; 4 import static org.junit.internal.MethodSorter.NAME_ASCENDING; 5 6 import java.lang.annotation.Annotation; 7 import java.lang.reflect.Constructor; 8 import java.lang.reflect.Field; 9 import java.lang.reflect.Method; 10 import java.lang.reflect.Modifier; 11 import java.util.ArrayList; 12 import java.util.Arrays; 13 import java.util.Collections; 14 import java.util.Comparator; 15 import java.util.LinkedHashMap; 16 import java.util.LinkedHashSet; 17 import java.util.List; 18 import java.util.Map; 19 import java.util.Set; 20 21 import org.junit.Assert; 22 import org.junit.Before; 23 import org.junit.BeforeClass; 24 import org.junit.internal.MethodSorter; 25 26 /** 27 * Wraps a class to be run, providing method validation and annotation searching 28 * 29 * @since 4.5 30 */ 31 public class TestClass implements Annotatable { 32 private static final FieldComparator FIELD_COMPARATOR = new FieldComparator(); 33 private static final MethodComparator METHOD_COMPARATOR = new MethodComparator(); 34 35 private final Class<?> clazz; 36 private final Map<Class<? extends Annotation>, List<FrameworkMethod>> methodsForAnnotations; 37 private final Map<Class<? extends Annotation>, List<FrameworkField>> fieldsForAnnotations; 38 39 /** 40 * Creates a {@code TestClass} wrapping {@code clazz}. Each time this 41 * constructor executes, the class is scanned for annotations, which can be 42 * an expensive process (we hope in future JDK's it will not be.) Therefore, 43 * try to share instances of {@code TestClass} where possible. 44 */ TestClass(Class<?> clazz)45 public TestClass(Class<?> clazz) { 46 this.clazz = clazz; 47 if (clazz != null && clazz.getConstructors().length > 1) { 48 throw new IllegalArgumentException( 49 "Test class can only have one constructor"); 50 } 51 52 Map<Class<? extends Annotation>, List<FrameworkMethod>> methodsForAnnotations = 53 new LinkedHashMap<Class<? extends Annotation>, List<FrameworkMethod>>(); 54 Map<Class<? extends Annotation>, List<FrameworkField>> fieldsForAnnotations = 55 new LinkedHashMap<Class<? extends Annotation>, List<FrameworkField>>(); 56 57 scanAnnotatedMembers(methodsForAnnotations, fieldsForAnnotations); 58 59 this.methodsForAnnotations = makeDeeplyUnmodifiable(methodsForAnnotations); 60 this.fieldsForAnnotations = makeDeeplyUnmodifiable(fieldsForAnnotations); 61 } 62 scanAnnotatedMembers(Map<Class<? extends Annotation>, List<FrameworkMethod>> methodsForAnnotations, Map<Class<? extends Annotation>, List<FrameworkField>> fieldsForAnnotations)63 protected void scanAnnotatedMembers(Map<Class<? extends Annotation>, List<FrameworkMethod>> methodsForAnnotations, Map<Class<? extends Annotation>, List<FrameworkField>> fieldsForAnnotations) { 64 for (Class<?> eachClass : getSuperClasses(clazz)) { 65 for (Method eachMethod : MethodSorter.getDeclaredMethods(eachClass)) { 66 addToAnnotationLists(new FrameworkMethod(eachMethod), methodsForAnnotations); 67 } 68 // ensuring fields are sorted to make sure that entries are inserted 69 // and read from fieldForAnnotations in a deterministic order 70 for (Field eachField : getSortedDeclaredFields(eachClass)) { 71 addToAnnotationLists(new FrameworkField(eachField), fieldsForAnnotations); 72 } 73 } 74 } 75 getSortedDeclaredFields(Class<?> clazz)76 private static Field[] getSortedDeclaredFields(Class<?> clazz) { 77 Field[] declaredFields = clazz.getDeclaredFields(); 78 Arrays.sort(declaredFields, FIELD_COMPARATOR); 79 return declaredFields; 80 } 81 addToAnnotationLists(T member, Map<Class<? extends Annotation>, List<T>> map)82 protected static <T extends FrameworkMember<T>> void addToAnnotationLists(T member, 83 Map<Class<? extends Annotation>, List<T>> map) { 84 for (Annotation each : member.getAnnotations()) { 85 Class<? extends Annotation> type = each.annotationType(); 86 List<T> members = getAnnotatedMembers(map, type, true); 87 if (member.isShadowedBy(members)) { 88 return; 89 } 90 if (runsTopToBottom(type)) { 91 members.add(0, member); 92 } else { 93 members.add(member); 94 } 95 } 96 } 97 98 private static <T extends FrameworkMember<T>> Map<Class<? extends Annotation>, List<T>> makeDeeplyUnmodifiable(Map<Class<? extends Annotation>, List<T>> source)99 makeDeeplyUnmodifiable(Map<Class<? extends Annotation>, List<T>> source) { 100 LinkedHashMap<Class<? extends Annotation>, List<T>> copy = 101 new LinkedHashMap<Class<? extends Annotation>, List<T>>(); 102 for (Map.Entry<Class<? extends Annotation>, List<T>> entry : source.entrySet()) { 103 copy.put(entry.getKey(), Collections.unmodifiableList(entry.getValue())); 104 } 105 return Collections.unmodifiableMap(copy); 106 } 107 108 /** 109 * Returns, efficiently, all the non-overridden methods in this class and 110 * its superclasses that are annotated}. 111 * 112 * @since 4.12 113 */ getAnnotatedMethods()114 public List<FrameworkMethod> getAnnotatedMethods() { 115 List<FrameworkMethod> methods = collectValues(methodsForAnnotations); 116 Collections.sort(methods, METHOD_COMPARATOR); 117 return methods; 118 } 119 120 /** 121 * Returns, efficiently, all the non-overridden methods in this class and 122 * its superclasses that are annotated with {@code annotationClass}. 123 */ getAnnotatedMethods( Class<? extends Annotation> annotationClass)124 public List<FrameworkMethod> getAnnotatedMethods( 125 Class<? extends Annotation> annotationClass) { 126 return Collections.unmodifiableList(getAnnotatedMembers(methodsForAnnotations, annotationClass, false)); 127 } 128 129 /** 130 * Returns, efficiently, all the non-overridden fields in this class and its 131 * superclasses that are annotated. 132 * 133 * @since 4.12 134 */ getAnnotatedFields()135 public List<FrameworkField> getAnnotatedFields() { 136 return collectValues(fieldsForAnnotations); 137 } 138 139 /** 140 * Returns, efficiently, all the non-overridden fields in this class and its 141 * superclasses that are annotated with {@code annotationClass}. 142 */ getAnnotatedFields( Class<? extends Annotation> annotationClass)143 public List<FrameworkField> getAnnotatedFields( 144 Class<? extends Annotation> annotationClass) { 145 return Collections.unmodifiableList(getAnnotatedMembers(fieldsForAnnotations, annotationClass, false)); 146 } 147 collectValues(Map<?, List<T>> map)148 private <T> List<T> collectValues(Map<?, List<T>> map) { 149 Set<T> values = new LinkedHashSet<T>(); 150 for (List<T> additionalValues : map.values()) { 151 values.addAll(additionalValues); 152 } 153 return new ArrayList<T>(values); 154 } 155 getAnnotatedMembers(Map<Class<? extends Annotation>, List<T>> map, Class<? extends Annotation> type, boolean fillIfAbsent)156 private static <T> List<T> getAnnotatedMembers(Map<Class<? extends Annotation>, List<T>> map, 157 Class<? extends Annotation> type, boolean fillIfAbsent) { 158 if (!map.containsKey(type) && fillIfAbsent) { 159 map.put(type, new ArrayList<T>()); 160 } 161 List<T> members = map.get(type); 162 return members == null ? Collections.<T>emptyList() : members; 163 } 164 runsTopToBottom(Class<? extends Annotation> annotation)165 private static boolean runsTopToBottom(Class<? extends Annotation> annotation) { 166 return annotation.equals(Before.class) 167 || annotation.equals(BeforeClass.class); 168 } 169 getSuperClasses(Class<?> testClass)170 private static List<Class<?>> getSuperClasses(Class<?> testClass) { 171 ArrayList<Class<?>> results = new ArrayList<Class<?>>(); 172 Class<?> current = testClass; 173 while (current != null) { 174 results.add(current); 175 current = current.getSuperclass(); 176 } 177 return results; 178 } 179 180 /** 181 * Returns the underlying Java class. 182 */ getJavaClass()183 public Class<?> getJavaClass() { 184 return clazz; 185 } 186 187 /** 188 * Returns the class's name. 189 */ getName()190 public String getName() { 191 if (clazz == null) { 192 return "null"; 193 } 194 return clazz.getName(); 195 } 196 197 /** 198 * Returns the only public constructor in the class, or throws an {@code 199 * AssertionError} if there are more or less than one. 200 */ 201 getOnlyConstructor()202 public Constructor<?> getOnlyConstructor() { 203 Constructor<?>[] constructors = clazz.getConstructors(); 204 Assert.assertEquals(1, constructors.length); 205 return constructors[0]; 206 } 207 208 /** 209 * Returns the annotations on this class 210 */ getAnnotations()211 public Annotation[] getAnnotations() { 212 if (clazz == null) { 213 return new Annotation[0]; 214 } 215 return clazz.getAnnotations(); 216 } 217 getAnnotation(Class<T> annotationType)218 public <T extends Annotation> T getAnnotation(Class<T> annotationType) { 219 if (clazz == null) { 220 return null; 221 } 222 return clazz.getAnnotation(annotationType); 223 } 224 getAnnotatedFieldValues(Object test, Class<? extends Annotation> annotationClass, Class<T> valueClass)225 public <T> List<T> getAnnotatedFieldValues(Object test, 226 Class<? extends Annotation> annotationClass, Class<T> valueClass) { 227 List<T> results = new ArrayList<T>(); 228 for (FrameworkField each : getAnnotatedFields(annotationClass)) { 229 try { 230 Object fieldValue = each.get(test); 231 if (valueClass.isInstance(fieldValue)) { 232 results.add(valueClass.cast(fieldValue)); 233 } 234 } catch (IllegalAccessException e) { 235 throw new RuntimeException( 236 "How did getFields return a field we couldn't access?", e); 237 } 238 } 239 return results; 240 } 241 getAnnotatedMethodValues(Object test, Class<? extends Annotation> annotationClass, Class<T> valueClass)242 public <T> List<T> getAnnotatedMethodValues(Object test, 243 Class<? extends Annotation> annotationClass, Class<T> valueClass) { 244 List<T> results = new ArrayList<T>(); 245 for (FrameworkMethod each : getAnnotatedMethods(annotationClass)) { 246 try { 247 /* 248 * A method annotated with @Rule may return a @TestRule or a @MethodRule, 249 * we cannot call the method to check whether the return type matches our 250 * expectation i.e. subclass of valueClass. If we do that then the method 251 * will be invoked twice and we do not want to do that. So we first check 252 * whether return type matches our expectation and only then call the method 253 * to fetch the MethodRule 254 */ 255 if (valueClass.isAssignableFrom(each.getReturnType())) { 256 Object fieldValue = each.invokeExplosively(test); 257 results.add(valueClass.cast(fieldValue)); 258 } 259 } catch (Throwable e) { 260 throw new RuntimeException( 261 "Exception in " + each.getName(), e); 262 } 263 } 264 return results; 265 } 266 isPublic()267 public boolean isPublic() { 268 return Modifier.isPublic(clazz.getModifiers()); 269 } 270 isANonStaticInnerClass()271 public boolean isANonStaticInnerClass() { 272 return clazz.isMemberClass() && !isStatic(clazz.getModifiers()); 273 } 274 275 @Override hashCode()276 public int hashCode() { 277 return (clazz == null) ? 0 : clazz.hashCode(); 278 } 279 280 @Override equals(Object obj)281 public boolean equals(Object obj) { 282 if (this == obj) { 283 return true; 284 } 285 if (obj == null) { 286 return false; 287 } 288 if (getClass() != obj.getClass()) { 289 return false; 290 } 291 TestClass other = (TestClass) obj; 292 return clazz == other.clazz; 293 } 294 295 /** 296 * Compares two fields by its name. 297 */ 298 private static class FieldComparator implements Comparator<Field> { compare(Field left, Field right)299 public int compare(Field left, Field right) { 300 return left.getName().compareTo(right.getName()); 301 } 302 } 303 304 /** 305 * Compares two methods by its name. 306 */ 307 private static class MethodComparator implements 308 Comparator<FrameworkMethod> { compare(FrameworkMethod left, FrameworkMethod right)309 public int compare(FrameworkMethod left, FrameworkMethod right) { 310 return NAME_ASCENDING.compare(left.getMethod(), right.getMethod()); 311 } 312 } 313 } 314