package org.junit.runners.model; import static java.lang.reflect.Modifier.isStatic; import java.lang.annotation.Annotation; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.junit.Assert; import org.junit.Before; import org.junit.BeforeClass; import org.junit.internal.MethodSorter; /** * Wraps a class to be run, providing method validation and annotation searching * * @since 4.5 */ public class TestClass { private final Class<?> fClass; private Map<Class<?>, List<FrameworkMethod>> fMethodsForAnnotations = new HashMap<Class<?>, List<FrameworkMethod>>(); private Map<Class<?>, List<FrameworkField>> fFieldsForAnnotations = new HashMap<Class<?>, List<FrameworkField>>(); /** * Creates a {@code TestClass} wrapping {@code klass}. Each time this * constructor executes, the class is scanned for annotations, which can be * an expensive process (we hope in future JDK's it will not be.) Therefore, * try to share instances of {@code TestClass} where possible. */ public TestClass(Class<?> klass) { fClass = klass; if (klass != null && klass.getConstructors().length > 1) { throw new IllegalArgumentException( "Test class can only have one constructor"); } for (Class<?> eachClass : getSuperClasses(fClass)) { for (Method eachMethod : MethodSorter.getDeclaredMethods(eachClass)) { addToAnnotationLists(new FrameworkMethod(eachMethod), fMethodsForAnnotations); } for (Field eachField : eachClass.getDeclaredFields()) { addToAnnotationLists(new FrameworkField(eachField), fFieldsForAnnotations); } } } private <T extends FrameworkMember<T>> void addToAnnotationLists(T member, Map<Class<?>, List<T>> map) { for (Annotation each : member.getAnnotations()) { Class<? extends Annotation> type = each.annotationType(); List<T> members = getAnnotatedMembers(map, type); if (member.isShadowedBy(members)) { return; } if (runsTopToBottom(type)) { members.add(0, member); } else { members.add(member); } } } /** * Returns, efficiently, all the non-overridden methods in this class and * its superclasses that are annotated with {@code annotationClass}. */ public List<FrameworkMethod> getAnnotatedMethods( Class<? extends Annotation> annotationClass) { return getAnnotatedMembers(fMethodsForAnnotations, annotationClass); } /** * Returns, efficiently, all the non-overridden fields in this class and its * superclasses that are annotated with {@code annotationClass}. */ public List<FrameworkField> getAnnotatedFields( Class<? extends Annotation> annotationClass) { return getAnnotatedMembers(fFieldsForAnnotations, annotationClass); } private <T> List<T> getAnnotatedMembers(Map<Class<?>, List<T>> map, Class<? extends Annotation> type) { if (!map.containsKey(type)) { map.put(type, new ArrayList<T>()); } return map.get(type); } private boolean runsTopToBottom(Class<? extends Annotation> annotation) { return annotation.equals(Before.class) || annotation.equals(BeforeClass.class); } private List<Class<?>> getSuperClasses(Class<?> testClass) { ArrayList<Class<?>> results = new ArrayList<Class<?>>(); Class<?> current = testClass; while (current != null) { results.add(current); current = current.getSuperclass(); } return results; } /** * Returns the underlying Java class. */ public Class<?> getJavaClass() { return fClass; } /** * Returns the class's name. */ public String getName() { if (fClass == null) { return "null"; } return fClass.getName(); } /** * Returns the only public constructor in the class, or throws an {@code * AssertionError} if there are more or less than one. */ public Constructor<?> getOnlyConstructor() { Constructor<?>[] constructors = fClass.getConstructors(); Assert.assertEquals(1, constructors.length); return constructors[0]; } /** * Returns the annotations on this class */ public Annotation[] getAnnotations() { if (fClass == null) { return new Annotation[0]; } return fClass.getAnnotations(); } public <T> List<T> getAnnotatedFieldValues(Object test, Class<? extends Annotation> annotationClass, Class<T> valueClass) { List<T> results = new ArrayList<T>(); for (FrameworkField each : getAnnotatedFields(annotationClass)) { try { Object fieldValue = each.get(test); if (valueClass.isInstance(fieldValue)) { results.add(valueClass.cast(fieldValue)); } } catch (IllegalAccessException e) { throw new RuntimeException( "How did getFields return a field we couldn't access?", e); } } return results; } public <T> List<T> getAnnotatedMethodValues(Object test, Class<? extends Annotation> annotationClass, Class<T> valueClass) { List<T> results = new ArrayList<T>(); for (FrameworkMethod each : getAnnotatedMethods(annotationClass)) { try { Object fieldValue = each.invokeExplosively(test, new Object[]{}); if (valueClass.isInstance(fieldValue)) { results.add(valueClass.cast(fieldValue)); } } catch (Throwable e) { throw new RuntimeException( "Exception in " + each.getName(), e); } } return results; } public boolean isANonStaticInnerClass() { return fClass.isMemberClass() && !isStatic(fClass.getModifiers()); } }