package org.junit.runners.model;

import static java.lang.reflect.Modifier.isStatic;
import static org.junit.internal.MethodSorter.NAME_ASCENDING;

import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

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
/** * Wraps a class to be run, providing method validation and annotation searching * * @since 4.5 */
public class TestClass implements Annotatable { private static final FieldComparator FIELD_COMPARATOR = new FieldComparator(); private static final MethodComparator METHOD_COMPARATOR = new MethodComparator(); private final Class<?> clazz; private final Map<Class<? extends Annotation>, List<FrameworkMethod>> methodsForAnnotations; private final Map<Class<? extends Annotation>, List<FrameworkField>> fieldsForAnnotations;
Creates a TestClass wrapping clazz. 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 TestClass where possible.
/** * Creates a {@code TestClass} wrapping {@code clazz}. 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<?> clazz) { this.clazz = clazz; if (clazz != null && clazz.getConstructors().length > 1) { throw new IllegalArgumentException( "Test class can only have one constructor"); } Map<Class<? extends Annotation>, List<FrameworkMethod>> methodsForAnnotations = new LinkedHashMap<Class<? extends Annotation>, List<FrameworkMethod>>(); Map<Class<? extends Annotation>, List<FrameworkField>> fieldsForAnnotations = new LinkedHashMap<Class<? extends Annotation>, List<FrameworkField>>(); scanAnnotatedMembers(methodsForAnnotations, fieldsForAnnotations); this.methodsForAnnotations = makeDeeplyUnmodifiable(methodsForAnnotations); this.fieldsForAnnotations = makeDeeplyUnmodifiable(fieldsForAnnotations); } protected void scanAnnotatedMembers(Map<Class<? extends Annotation>, List<FrameworkMethod>> methodsForAnnotations, Map<Class<? extends Annotation>, List<FrameworkField>> fieldsForAnnotations) { for (Class<?> eachClass : getSuperClasses(clazz)) { for (Method eachMethod : MethodSorter.getDeclaredMethods(eachClass)) { addToAnnotationLists(new FrameworkMethod(eachMethod), methodsForAnnotations); } // ensuring fields are sorted to make sure that entries are inserted // and read from fieldForAnnotations in a deterministic order for (Field eachField : getSortedDeclaredFields(eachClass)) { addToAnnotationLists(new FrameworkField(eachField), fieldsForAnnotations); } } } private static Field[] getSortedDeclaredFields(Class<?> clazz) { Field[] declaredFields = clazz.getDeclaredFields(); Arrays.sort(declaredFields, FIELD_COMPARATOR); return declaredFields; } protected static <T extends FrameworkMember<T>> void addToAnnotationLists(T member, Map<Class<? extends Annotation>, List<T>> map) { for (Annotation each : member.getAnnotations()) { Class<? extends Annotation> type = each.annotationType(); List<T> members = getAnnotatedMembers(map, type, true); if (member.isShadowedBy(members)) { return; } if (runsTopToBottom(type)) { members.add(0, member); } else { members.add(member); } } } private static <T extends FrameworkMember<T>> Map<Class<? extends Annotation>, List<T>> makeDeeplyUnmodifiable(Map<Class<? extends Annotation>, List<T>> source) { LinkedHashMap<Class<? extends Annotation>, List<T>> copy = new LinkedHashMap<Class<? extends Annotation>, List<T>>(); for (Map.Entry<Class<? extends Annotation>, List<T>> entry : source.entrySet()) { copy.put(entry.getKey(), Collections.unmodifiableList(entry.getValue())); } return Collections.unmodifiableMap(copy); }
Returns, efficiently, all the non-overridden methods in this class and its superclasses that are annotated}.
Since:4.12
/** * Returns, efficiently, all the non-overridden methods in this class and * its superclasses that are annotated}. * * @since 4.12 */
public List<FrameworkMethod> getAnnotatedMethods() { List<FrameworkMethod> methods = collectValues(methodsForAnnotations); Collections.sort(methods, METHOD_COMPARATOR); return methods; }
Returns, efficiently, all the non-overridden methods in this class and its superclasses that are annotated with annotationClass.
/** * 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 Collections.unmodifiableList(getAnnotatedMembers(methodsForAnnotations, annotationClass, false)); }
Returns, efficiently, all the non-overridden fields in this class and its superclasses that are annotated.
Since:4.12
/** * Returns, efficiently, all the non-overridden fields in this class and its * superclasses that are annotated. * * @since 4.12 */
public List<FrameworkField> getAnnotatedFields() { return collectValues(fieldsForAnnotations); }
Returns, efficiently, all the non-overridden fields in this class and its superclasses that are annotated with 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 Collections.unmodifiableList(getAnnotatedMembers(fieldsForAnnotations, annotationClass, false)); } private <T> List<T> collectValues(Map<?, List<T>> map) { Set<T> values = new LinkedHashSet<T>(); for (List<T> additionalValues : map.values()) { values.addAll(additionalValues); } return new ArrayList<T>(values); } private static <T> List<T> getAnnotatedMembers(Map<Class<? extends Annotation>, List<T>> map, Class<? extends Annotation> type, boolean fillIfAbsent) { if (!map.containsKey(type) && fillIfAbsent) { map.put(type, new ArrayList<T>()); } List<T> members = map.get(type); return members == null ? Collections.<T>emptyList() : members; } private static boolean runsTopToBottom(Class<? extends Annotation> annotation) { return annotation.equals(Before.class) || annotation.equals(BeforeClass.class); } private static 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.
/** * Returns the underlying Java class. */
public Class<?> getJavaClass() { return clazz; }
Returns the class's name.
/** * Returns the class's name. */
public String getName() { if (clazz == null) { return "null"; } return clazz.getName(); }
Returns the only public constructor in the class, or throws an AssertionError if there are more or less than one.
/** * 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 = clazz.getConstructors(); Assert.assertEquals(1, constructors.length); return constructors[0]; }
Returns the annotations on this class
/** * Returns the annotations on this class */
public Annotation[] getAnnotations() { if (clazz == null) { return new Annotation[0]; } return clazz.getAnnotations(); } public <T extends Annotation> T getAnnotation(Class<T> annotationType) { if (clazz == null) { return null; } return clazz.getAnnotation(annotationType); } 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 { /* * A method annotated with @Rule may return a @TestRule or a @MethodRule, * we cannot call the method to check whether the return type matches our * expectation i.e. subclass of valueClass. If we do that then the method * will be invoked twice and we do not want to do that. So we first check * whether return type matches our expectation and only then call the method * to fetch the MethodRule */ if (valueClass.isAssignableFrom(each.getReturnType())) { Object fieldValue = each.invokeExplosively(test); results.add(valueClass.cast(fieldValue)); } } catch (Throwable e) { throw new RuntimeException( "Exception in " + each.getName(), e); } } return results; } public boolean isPublic() { return Modifier.isPublic(clazz.getModifiers()); } public boolean isANonStaticInnerClass() { return clazz.isMemberClass() && !isStatic(clazz.getModifiers()); } @Override public int hashCode() { return (clazz == null) ? 0 : clazz.hashCode(); } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } TestClass other = (TestClass) obj; return clazz == other.clazz; }
Compares two fields by its name.
/** * Compares two fields by its name. */
private static class FieldComparator implements Comparator<Field> { public int compare(Field left, Field right) { return left.getName().compareTo(right.getName()); } }
Compares two methods by its name.
/** * Compares two methods by its name. */
private static class MethodComparator implements Comparator<FrameworkMethod> { public int compare(FrameworkMethod left, FrameworkMethod right) { return NAME_ASCENDING.compare(left.getMethod(), right.getMethod()); } } }