001package org.junit.runners.model; 002 003import static java.lang.reflect.Modifier.isStatic; 004 005import java.lang.annotation.Annotation; 006import java.lang.reflect.Constructor; 007import java.lang.reflect.Field; 008import java.lang.reflect.Method; 009import java.util.ArrayList; 010import java.util.HashMap; 011import java.util.List; 012import java.util.Map; 013 014import org.junit.Assert; 015import org.junit.Before; 016import org.junit.BeforeClass; 017import org.junit.internal.MethodSorter; 018 019/** 020 * Wraps a class to be run, providing method validation and annotation searching 021 * 022 * @since 4.5 023 */ 024public class TestClass { 025 private final Class<?> fClass; 026 027 private Map<Class<?>, List<FrameworkMethod>> fMethodsForAnnotations = new HashMap<Class<?>, List<FrameworkMethod>>(); 028 029 private Map<Class<?>, List<FrameworkField>> fFieldsForAnnotations = new HashMap<Class<?>, List<FrameworkField>>(); 030 031 /** 032 * Creates a {@code TestClass} wrapping {@code klass}. Each time this 033 * constructor executes, the class is scanned for annotations, which can be 034 * an expensive process (we hope in future JDK's it will not be.) Therefore, 035 * try to share instances of {@code TestClass} where possible. 036 */ 037 public TestClass(Class<?> klass) { 038 fClass = klass; 039 if (klass != null && klass.getConstructors().length > 1) { 040 throw new IllegalArgumentException( 041 "Test class can only have one constructor"); 042 } 043 044 for (Class<?> eachClass : getSuperClasses(fClass)) { 045 for (Method eachMethod : MethodSorter.getDeclaredMethods(eachClass)) { 046 addToAnnotationLists(new FrameworkMethod(eachMethod), 047 fMethodsForAnnotations); 048 } 049 for (Field eachField : eachClass.getDeclaredFields()) { 050 addToAnnotationLists(new FrameworkField(eachField), 051 fFieldsForAnnotations); 052 } 053 } 054 } 055 056 private <T extends FrameworkMember<T>> void addToAnnotationLists(T member, 057 Map<Class<?>, List<T>> map) { 058 for (Annotation each : member.getAnnotations()) { 059 Class<? extends Annotation> type = each.annotationType(); 060 List<T> members = getAnnotatedMembers(map, type); 061 if (member.isShadowedBy(members)) { 062 return; 063 } 064 if (runsTopToBottom(type)) { 065 members.add(0, member); 066 } else { 067 members.add(member); 068 } 069 } 070 } 071 072 /** 073 * Returns, efficiently, all the non-overridden methods in this class and 074 * its superclasses that are annotated with {@code annotationClass}. 075 */ 076 public List<FrameworkMethod> getAnnotatedMethods( 077 Class<? extends Annotation> annotationClass) { 078 return getAnnotatedMembers(fMethodsForAnnotations, annotationClass); 079 } 080 081 /** 082 * Returns, efficiently, all the non-overridden fields in this class and its 083 * superclasses that are annotated with {@code annotationClass}. 084 */ 085 public List<FrameworkField> getAnnotatedFields( 086 Class<? extends Annotation> annotationClass) { 087 return getAnnotatedMembers(fFieldsForAnnotations, annotationClass); 088 } 089 090 private <T> List<T> getAnnotatedMembers(Map<Class<?>, List<T>> map, 091 Class<? extends Annotation> type) { 092 if (!map.containsKey(type)) { 093 map.put(type, new ArrayList<T>()); 094 } 095 return map.get(type); 096 } 097 098 private boolean runsTopToBottom(Class<? extends Annotation> annotation) { 099 return annotation.equals(Before.class) 100 || annotation.equals(BeforeClass.class); 101 } 102 103 private List<Class<?>> getSuperClasses(Class<?> testClass) { 104 ArrayList<Class<?>> results = new ArrayList<Class<?>>(); 105 Class<?> current = testClass; 106 while (current != null) { 107 results.add(current); 108 current = current.getSuperclass(); 109 } 110 return results; 111 } 112 113 /** 114 * Returns the underlying Java class. 115 */ 116 public Class<?> getJavaClass() { 117 return fClass; 118 } 119 120 /** 121 * Returns the class's name. 122 */ 123 public String getName() { 124 if (fClass == null) { 125 return "null"; 126 } 127 return fClass.getName(); 128 } 129 130 /** 131 * Returns the only public constructor in the class, or throws an {@code 132 * AssertionError} if there are more or less than one. 133 */ 134 135 public Constructor<?> getOnlyConstructor() { 136 Constructor<?>[] constructors = fClass.getConstructors(); 137 Assert.assertEquals(1, constructors.length); 138 return constructors[0]; 139 } 140 141 /** 142 * Returns the annotations on this class 143 */ 144 public Annotation[] getAnnotations() { 145 if (fClass == null) { 146 return new Annotation[0]; 147 } 148 return fClass.getAnnotations(); 149 } 150 151 public <T> List<T> getAnnotatedFieldValues(Object test, 152 Class<? extends Annotation> annotationClass, Class<T> valueClass) { 153 List<T> results = new ArrayList<T>(); 154 for (FrameworkField each : getAnnotatedFields(annotationClass)) { 155 try { 156 Object fieldValue = each.get(test); 157 if (valueClass.isInstance(fieldValue)) { 158 results.add(valueClass.cast(fieldValue)); 159 } 160 } catch (IllegalAccessException e) { 161 throw new RuntimeException( 162 "How did getFields return a field we couldn't access?", e); 163 } 164 } 165 return results; 166 } 167 168 public <T> List<T> getAnnotatedMethodValues(Object test, 169 Class<? extends Annotation> annotationClass, Class<T> valueClass) { 170 List<T> results = new ArrayList<T>(); 171 for (FrameworkMethod each : getAnnotatedMethods(annotationClass)) { 172 try { 173 Object fieldValue = each.invokeExplosively(test, new Object[]{}); 174 if (valueClass.isInstance(fieldValue)) { 175 results.add(valueClass.cast(fieldValue)); 176 } 177 } catch (Throwable e) { 178 throw new RuntimeException( 179 "Exception in " + each.getName(), e); 180 } 181 } 182 return results; 183 } 184 185 public boolean isANonStaticInnerClass() { 186 return fClass.isMemberClass() && !isStatic(fClass.getModifiers()); 187 } 188}