All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.junit.runners.model.TestClass Maven / Gradle / Ivy

Go to download

JUnit is a unit testing framework for Java, created by Erich Gamma and Kent Beck.

There is a newer version: 4.13.2
Show newest version
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
 */
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, List> methodsForAnnotations;
    private final Map, List> fieldsForAnnotations;

    /**
     * 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, List> methodsForAnnotations =
                new LinkedHashMap, List>();
        Map, List> fieldsForAnnotations =
                new LinkedHashMap, List>();

        scanAnnotatedMembers(methodsForAnnotations, fieldsForAnnotations);

        this.methodsForAnnotations = makeDeeplyUnmodifiable(methodsForAnnotations);
        this.fieldsForAnnotations = makeDeeplyUnmodifiable(fieldsForAnnotations);
    }

    protected void scanAnnotatedMembers(Map, List> methodsForAnnotations, Map, List> 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 > void addToAnnotationLists(T member,
            Map, List> map) {
        for (Annotation each : member.getAnnotations()) {
            Class type = each.annotationType();
            List members = getAnnotatedMembers(map, type, true);
            T memberToAdd = member.handlePossibleBridgeMethod(members);
            if (memberToAdd == null) {
                return;
            }
            if (runsTopToBottom(type)) {
                members.add(0, memberToAdd);
            } else {
                members.add(memberToAdd);
            }
        }
    }

    private static > Map, List>
            makeDeeplyUnmodifiable(Map, List> source) {
        Map, List> copy =
                new LinkedHashMap, List>();
        for (Map.Entry, List> 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
     */
    public List getAnnotatedMethods() {
        List 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 {@code annotationClass}.
     */
    public List getAnnotatedMethods(
            Class 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
     */
    public List getAnnotatedFields() {
        return collectValues(fieldsForAnnotations);
    }

    /**
     * Returns, efficiently, all the non-overridden fields in this class and its
     * superclasses that are annotated with {@code annotationClass}.
     */
    public List getAnnotatedFields(
            Class annotationClass) {
        return Collections.unmodifiableList(getAnnotatedMembers(fieldsForAnnotations, annotationClass, false));
    }

    private  List collectValues(Map> map) {
        Set values = new LinkedHashSet();
        for (List additionalValues : map.values()) {
            values.addAll(additionalValues);
        }
        return new ArrayList(values);
    }

    private static  List getAnnotatedMembers(Map, List> map,
            Class type, boolean fillIfAbsent) {
        if (!map.containsKey(type) && fillIfAbsent) {
            map.put(type, new ArrayList());
        }
        List members = map.get(type);
        return members == null ? Collections.emptyList() : members;
    }

    private static boolean runsTopToBottom(Class annotation) {
        return annotation.equals(Before.class)
                || annotation.equals(BeforeClass.class);
    }

    private static List> getSuperClasses(Class testClass) {
        List> results = new ArrayList>();
        Class current = testClass;
        while (current != null) {
            results.add(current);
            current = current.getSuperclass();
        }
        return results;
    }

    /**
     * Returns the underlying Java class.
     */
    public Class getJavaClass() {
        return clazz;
    }

    /**
     * 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 {@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
     */
    public Annotation[] getAnnotations() {
        if (clazz == null) {
            return new Annotation[0];
        }
        return clazz.getAnnotations();
    }

    public  T getAnnotation(Class annotationType) {
        if (clazz == null) {
            return null;
        }
        return clazz.getAnnotation(annotationType);
    }

    public  List getAnnotatedFieldValues(Object test,
            Class annotationClass, Class valueClass) {
        final List results = new ArrayList();
        collectAnnotatedFieldValues(test, annotationClass, valueClass,
                new MemberValueConsumer() {
                    public void accept(FrameworkMember member, T value) {
                        results.add(value);
                    }
                });
        return results;
    }

    /**
     * Finds the fields annotated with the specified annotation and having the specified type,
     * retrieves the values and passes those to the specified consumer.
     *
     * @since 4.13
     */
    public  void collectAnnotatedFieldValues(Object test,
            Class annotationClass, Class valueClass,
            MemberValueConsumer consumer) {
        for (FrameworkField each : getAnnotatedFields(annotationClass)) {
            try {
                Object fieldValue = each.get(test);
                if (valueClass.isInstance(fieldValue)) {
                    consumer.accept(each, valueClass.cast(fieldValue));
                }
            } catch (IllegalAccessException e) {
                throw new RuntimeException(
                        "How did getFields return a field we couldn't access?", e);
            }
        }
    }

    public  List getAnnotatedMethodValues(Object test,
            Class annotationClass, Class valueClass) {
        final List results = new ArrayList();
        collectAnnotatedMethodValues(test, annotationClass, valueClass,
                new MemberValueConsumer() {
                    public void accept(FrameworkMember member, T value) {
                        results.add(value);
                    }
                });
        return results;
    }

    /**
     * Finds the methods annotated with the specified annotation and returning the specified type,
     * invokes it and pass the return value to the specified consumer.
     *
     * @since 4.13
     */
    public  void collectAnnotatedMethodValues(Object test,
            Class annotationClass, Class valueClass,
            MemberValueConsumer consumer) {
        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);
                    consumer.accept(each, valueClass.cast(fieldValue));
                }
            } catch (Throwable e) {
                throw new RuntimeException(
                        "Exception in " + each.getName(), e);
            }
        }
    }

    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.
     */
    private static class FieldComparator implements Comparator {
        public int compare(Field left, Field right) {
            return left.getName().compareTo(right.getName());
        }
    }

    /**
     * Compares two methods by its name.
     */
    private static class MethodComparator implements
            Comparator {
        public int compare(FrameworkMethod left, FrameworkMethod right) {
            return NAME_ASCENDING.compare(left.getMethod(), right.getMethod());
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy