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    }