    001    package org.junit.runner.manipulation;
    002
    003    import java.lang.reflect.Constructor;
    004    import java.util.ArrayList;
    005    import java.util.Collection;
    006    import java.util.Collections;
    007    import java.util.List;
    008    import java.util.Random;
    009
    010    import org.junit.runner.Description;
    011    import org.junit.runner.OrderWith;
    012
    013    /**
    014     * Reorders tests. An {@code Ordering} can reverse the order of tests, sort the
    015     * order or even shuffle the order.
    016     *
    017     * <p>In general you will not need to use a <code>Ordering</code> directly.
    018     * Instead, use {@link org.junit.runner.Request#orderWith(Ordering)}.
    019     *
    020     * @since 4.13
    021     */
    022    public abstract class Ordering {
    023        private static final String CONSTRUCTOR_ERROR_FORMAT
    024                = "Ordering class %s should have a public constructor with signature "
    025                        + "%s(Ordering.Context context)";
    026
    027        /**
    028         * Creates an {@link Ordering} that shuffles the items using the given
    029         * {@link Random} instance.
    030         */
    031        public static Ordering shuffledBy(final Random random) {
    032            return new Ordering() {
    033                @Override
    034                boolean validateOrderingIsCorrect() {
    035                    return false;
    036                }
    037
    038                @Override
    039                protected List<Description> orderItems(Collection<Description> descriptions) {
    040                    List<Description> shuffled = new ArrayList<Description>(descriptions);
    041                    Collections.shuffle(shuffled, random);
    042                    return shuffled;
    043                }
    044            };
    045        }
    046
    047        /**
    048         * Creates an {@link Ordering} from the given factory class. The class must have a public no-arg
    049         * constructor.
    050         *
    051         * @param factoryClass class to use to create the ordering
    052         * @param annotatedTestClass test class that is annotated with {@link OrderWith}.
    053         * @throws InvalidOrderingException if the instance could not be created
    054         */
    055        public static Ordering definedBy(
    056                Class<? extends Ordering.Factory> factoryClass, Description annotatedTestClass)
    057                throws InvalidOrderingException {
    058            if (factoryClass == null) {
    059                throw new NullPointerException("factoryClass cannot be null");
    060            }
    061            if (annotatedTestClass == null) {
    062                throw new NullPointerException("annotatedTestClass cannot be null");
    063            }
    064
    065            Ordering.Factory factory;
    066            try {
    067                Constructor<? extends Ordering.Factory> constructor = factoryClass.getConstructor();
    068                factory = constructor.newInstance();
    069            } catch (NoSuchMethodException e) {
    070                throw new InvalidOrderingException(String.format(
    071                        CONSTRUCTOR_ERROR_FORMAT,
    072                        getClassName(factoryClass),
    073                        factoryClass.getSimpleName()));
    074            } catch (Exception e) {
    075                throw new InvalidOrderingException(
    076                        "Could not create ordering for " + annotatedTestClass, e);
    077            }
    078            return definedBy(factory, annotatedTestClass);
    079        }
    080
    081        /**
    082         * Creates an {@link Ordering} from the given factory.
    083         *
    084         * @param factory factory to use to create the ordering
    085         * @param annotatedTestClass test class that is annotated with {@link OrderWith}.
    086         * @throws InvalidOrderingException if the instance could not be created
    087         */
    088        public static Ordering definedBy(
    089                Ordering.Factory factory, Description annotatedTestClass)
    090                throws InvalidOrderingException {
    091            if (factory == null) {
    092                throw new NullPointerException("factory cannot be null");
    093            }
    094            if (annotatedTestClass == null) {
    095                throw new NullPointerException("annotatedTestClass cannot be null");
    096            }
    097
    098            return factory.create(new Ordering.Context(annotatedTestClass));
    099        }
    100
    101        private static String getClassName(Class<?> clazz) {
    102            String name = clazz.getCanonicalName();
    103            if (name == null) {
    104                return clazz.getName();
    105            }
    106            return name;
    107        }
    108
    109        /**
    110         * Order the tests in <code>target</code> using this ordering.
    111         *
    112         * @throws InvalidOrderingException if ordering does something invalid (like remove or add
    113         * children)
    114         */
    115        public void apply(Object target) throws InvalidOrderingException {
    116            /*
    117             * Note that some subclasses of Ordering override apply(). The Sorter
    118             * subclass of Ordering overrides apply() to apply the sort (this is
    119             * done because sorting is more efficient than ordering).
    120             */
    121            if (target instanceof Orderable) {
    122                Orderable orderable = (Orderable) target;
    123                orderable.order(new Orderer(this));
    124            }
    125        }
    126
    127        /**
    128         * Returns {@code true} if this ordering could produce invalid results (i.e.
    129         * if it could add or remove values).
    130         */
    131        boolean validateOrderingIsCorrect() {
    132            return true;
    133        }
    134
    135        /**
    136         * Implemented by sub-classes to order the descriptions.
    137         *
    138         * @return descriptions in order
    139         */
    140        protected abstract List<Description> orderItems(Collection<Description> descriptions);
    141
    142        /** Context about the ordering being applied. */
    143        public static class Context {
    144            private final Description description;
    145
    146            /**
    147             * Gets the description for the top-level target being ordered.
    148             */
    149            public Description getTarget() {
    150                return description;
    151            }
    152
    153            private Context(Description description) {
    154                this.description = description;
    155            }
    156        }
    157
    158        /**
    159         * Factory for creating {@link Ordering} instances.
    160         *
    161         * <p>For a factory to be used with {@code @OrderWith} it needs to have a public no-arg
    162         * constructor.
    163         */
    164        public interface Factory {
    165            /**
    166             * Creates an Ordering instance using the given context. Implementations
    167             * of this method that do not need to use the context can return the
    168             * same instance every time.
    169             */
    170            Ordering create(Context context);
    171        }
    172    }