001package org.junit.runner.manipulation; 002 003import java.lang.reflect.Constructor; 004import java.util.ArrayList; 005import java.util.Collection; 006import java.util.Collections; 007import java.util.List; 008import java.util.Random; 009 010import org.junit.runner.Description; 011import 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 */ 022public 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}