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 }