Skip to content

Commit 44b64df

Browse files
committed
Add Ordering, Orderable and @OrderWith.
These APIs allow arbitrary ordernig of tests, including randomization.
1 parent 9c337dc commit 44b64df

21 files changed

+1059
-37
lines changed

src/main/java/junit/framework/JUnit4TestAdapter.java

+8-2
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,13 @@
99
import org.junit.runner.Runner;
1010
import org.junit.runner.manipulation.Filter;
1111
import org.junit.runner.manipulation.Filterable;
12+
import org.junit.runner.manipulation.GenericOrdering;
13+
import org.junit.runner.manipulation.InvalidOrderingException;
1214
import org.junit.runner.manipulation.NoTestsRemainException;
13-
import org.junit.runner.manipulation.Sortable;
15+
import org.junit.runner.manipulation.Orderable;
1416
import org.junit.runner.manipulation.Sorter;
1517

16-
public class JUnit4TestAdapter implements Test, Filterable, Sortable, Describable {
18+
public class JUnit4TestAdapter implements Test, Filterable, Orderable, Describable {
1719
private final Class<?> fNewTestClass;
1820

1921
private final Runner fRunner;
@@ -83,4 +85,8 @@ public void filter(Filter filter) throws NoTestsRemainException {
8385
public void sort(Sorter sorter) {
8486
sorter.apply(fRunner);
8587
}
88+
89+
public void order(GenericOrdering ordering) throws InvalidOrderingException {
90+
ordering.apply(fRunner);
91+
}
8692
}

src/main/java/org/junit/internal/runners/JUnit38ClassRunner.java

+14-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
package org.junit.internal.runners;
22

3+
import java.lang.annotation.Annotation;
4+
import java.lang.reflect.Method;
5+
36
import junit.extensions.TestDecorator;
47
import junit.framework.AssertionFailedError;
58
import junit.framework.Test;
@@ -12,15 +15,16 @@
1215
import org.junit.runner.Runner;
1316
import org.junit.runner.manipulation.Filter;
1417
import org.junit.runner.manipulation.Filterable;
18+
import org.junit.runner.manipulation.GenericOrdering;
19+
import org.junit.runner.manipulation.InvalidOrderingException;
1520
import org.junit.runner.manipulation.NoTestsRemainException;
21+
import org.junit.runner.manipulation.Orderable;
1622
import org.junit.runner.manipulation.Sortable;
1723
import org.junit.runner.manipulation.Sorter;
1824
import org.junit.runner.notification.Failure;
1925
import org.junit.runner.notification.RunNotifier;
20-
import java.lang.annotation.Annotation;
21-
import java.lang.reflect.Method;
2226

23-
public class JUnit38ClassRunner extends Runner implements Filterable, Sortable {
27+
public class JUnit38ClassRunner extends Runner implements Filterable, Orderable {
2428
private static final class OldTestClassAdaptingListener implements
2529
TestListener {
2630
private final RunNotifier notifier;
@@ -170,6 +174,13 @@ public void sort(Sorter sorter) {
170174
}
171175
}
172176

177+
public void order(GenericOrdering ordering) throws InvalidOrderingException {
178+
if (getTest() instanceof Orderable) {
179+
Orderable adapter = (Orderable) getTest();
180+
adapter.order(ordering);
181+
}
182+
}
183+
173184
private void setTest(Test test) {
174185
this.test = test;
175186
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package org.junit.runner;
2+
3+
import java.lang.annotation.ElementType;
4+
import java.lang.annotation.Inherited;
5+
import java.lang.annotation.Retention;
6+
import java.lang.annotation.RetentionPolicy;
7+
import java.lang.annotation.Target;
8+
9+
import org.junit.runner.manipulation.Ordering;
10+
11+
/**
12+
* When a test class is annotated with <code>&#064;OrderWith</code> or extends a class annotated
13+
* with <code>&#064;OrderWith</code>, JUnit will order the tests in the test class (and child
14+
* test classes, if any) using the ordering defined by the {@link Ordering} class.
15+
*
16+
* @since 4.13
17+
*/
18+
@Retention(RetentionPolicy.RUNTIME)
19+
@Target(ElementType.TYPE)
20+
@Inherited
21+
public @interface OrderWith {
22+
/**
23+
* @return a class that extends {@link Ordering} (must have a public no-argument constructor)
24+
*/
25+
Class<? extends Ordering> value();
26+
}

src/main/java/org/junit/runner/Request.java

+48-6
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
import org.junit.internal.requests.SortingRequest;
99
import org.junit.internal.runners.ErrorReportingRunner;
1010
import org.junit.runner.manipulation.Filter;
11+
import org.junit.runner.manipulation.InvalidOrderingException;
12+
import org.junit.runner.manipulation.Ordering;
1113
import org.junit.runners.model.InitializationError;
1214

1315
/**
@@ -148,15 +150,15 @@ public Request filterWith(final Description desiredDescription) {
148150
* For example, here is code to run a test suite in alphabetical order:
149151
* <pre>
150152
* private static Comparator&lt;Description&gt; forward() {
151-
* return new Comparator&lt;Description&gt;() {
152-
* public int compare(Description o1, Description o2) {
153-
* return o1.getDisplayName().compareTo(o2.getDisplayName());
154-
* }
155-
* };
153+
* return new Comparator&lt;Description&gt;() {
154+
* public int compare(Description o1, Description o2) {
155+
* return o1.getDisplayName().compareTo(o2.getDisplayName());
156+
* }
157+
* };
156158
* }
157159
*
158160
* public static main() {
159-
* new JUnitCore().run(Request.aClass(AllTests.class).sortWith(forward()));
161+
* new JUnitCore().run(Request.aClass(AllTests.class).sortWith(forward()));
160162
* }
161163
* </pre>
162164
*
@@ -166,4 +168,44 @@ public Request filterWith(final Description desiredDescription) {
166168
public Request sortWith(Comparator<Description> comparator) {
167169
return new SortingRequest(this, comparator);
168170
}
171+
172+
/**
173+
* Returns a Request whose Tests can be run in a certain order, defined by
174+
* <code>ordering</code>
175+
* <p>
176+
* For example, here is code to run a test suite in reverse order:
177+
* <pre>
178+
* private static Ordering reverse() {
179+
* return new Ordering() {
180+
* public List&lt;Description&gt; order(Collection&lt;Description&gt; siblings) {
181+
* List&lt;Description&gt; ordered = new ArrayList&lt;&gt;(siblings);
182+
* Collections.reverse(ordered);
183+
* return ordered;
184+
* }
185+
* }
186+
* }
187+
*
188+
* public static main() {
189+
* new JUnitCore().run(Request.aClass(AllTests.class).orderWith(reverse()));
190+
* }
191+
* </pre>
192+
*
193+
* @return a Request with ordered Tests
194+
* @since 4.13
195+
*/
196+
public Request orderWith(final Ordering ordering) {
197+
final Request delegate = this;
198+
return new Request() {
199+
@Override
200+
public Runner getRunner() {
201+
try {
202+
Runner runner = delegate.getRunner();
203+
ordering.apply(runner);
204+
return runner;
205+
} catch (InvalidOrderingException e) {
206+
return new ErrorReportingRunner(ordering.getClass(), e);
207+
}
208+
}
209+
};
210+
}
169211
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package org.junit.runner.manipulation;
2+
3+
import java.util.Collection;
4+
import java.util.List;
5+
6+
import org.junit.runner.Description;
7+
8+
/**
9+
* An {@link Ordering} that is not a {@link Sorter}.
10+
*
11+
* @since 4.13
12+
*/
13+
public final class GenericOrdering extends Ordering {
14+
private final Ordering delegate;
15+
16+
GenericOrdering(Ordering delegate) {
17+
this.delegate = delegate;
18+
}
19+
20+
@Override
21+
public List<Description> order(Collection<Description> siblings) {
22+
return delegate.order(siblings);
23+
}
24+
25+
@Override
26+
public void apply(Object runner) throws InvalidOrderingException {
27+
if (runner instanceof Orderable) {
28+
Orderable orderable = (Orderable) runner;
29+
orderable.order(this);
30+
}
31+
}
32+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package org.junit.runner.manipulation;
2+
3+
/**
4+
* Thrown when an ordering does something invalid (like remove or add children)
5+
*
6+
* @since 4.13
7+
*/
8+
public class InvalidOrderingException extends Exception {
9+
private static final long serialVersionUID = 1L;
10+
11+
public InvalidOrderingException() {
12+
}
13+
14+
public InvalidOrderingException(String message) {
15+
super(message);
16+
}
17+
18+
public InvalidOrderingException(String message, Throwable cause) {
19+
super(message, cause);
20+
}
21+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package org.junit.runner.manipulation;
2+
3+
/**
4+
* Interface for runners that allow ordering of tests.
5+
*
6+
* <p>Beware of using this interface to cope with order dependencies between tests.
7+
* Tests that are isolated from each other are less expensive to maintain and
8+
* can be run individually.
9+
*
10+
* @since 4.13
11+
*/
12+
public interface Orderable extends Sortable {
13+
14+
/**
15+
* Orders the tests using <code>ordering</code>
16+
*
17+
* @throws InvalidOrderingException if ordering does something invalid (like remove or add children)
18+
*/
19+
void order(GenericOrdering ordering) throws InvalidOrderingException;
20+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
package org.junit.runner.manipulation;
2+
3+
import java.util.ArrayList;
4+
import java.util.Collection;
5+
import java.util.Collections;
6+
import java.util.List;
7+
import java.util.Random;
8+
9+
import org.junit.runner.Description;
10+
11+
/**
12+
* Reorders tests. An {@code Ordering} can reverse the order of tests, sort the
13+
* order or even shuffle the order.
14+
*
15+
* <p>In general you will not need to use a <code>Ordering</code> directly.
16+
* Instead, use {@link org.junit.runner.Request#orderWith(Ordering)}.
17+
*
18+
* @since 4.13
19+
*/
20+
public abstract class Ordering {
21+
22+
/**
23+
* Creates an {@link Ordering} that shuffles the items using the given
24+
* {@link Random} instance.
25+
*/
26+
public static Ordering shuffledBy(final Random random) {
27+
return new Ordering() {
28+
@Override
29+
public List<Description> order(Collection<Description> siblings) {
30+
List<Description> shuffled = new ArrayList<Description>(siblings);
31+
Collections.shuffle(shuffled, random);
32+
return shuffled;
33+
}
34+
};
35+
}
36+
37+
/**
38+
* Creates an {@link Ordering} from the given class. The class must have a public no-argument constructor.
39+
*
40+
* @throws InvalidOrderingException if the instance could not be created
41+
*/
42+
public static Ordering definedBy(Class<? extends Ordering> orderingClass)
43+
throws InvalidOrderingException {
44+
try {
45+
return orderingClass.newInstance();
46+
} catch (InstantiationException e) {
47+
throw new InvalidOrderingException("Could not create ordering", e);
48+
} catch (IllegalAccessException e) {
49+
throw new InvalidOrderingException("Could not create ordering", e);
50+
}
51+
}
52+
53+
/**
54+
* Order the tests in <code>runner</code> using this ordering.
55+
*
56+
* @throws InvalidOrderingException if ordering does something invalid (like remove or add children)
57+
*/
58+
public void apply(Object runner) throws InvalidOrderingException {
59+
if (runner instanceof Orderable) {
60+
Orderable orderable = (Orderable) runner;
61+
orderable.order(new GenericOrdering(this));
62+
}
63+
}
64+
65+
/**
66+
* Orders the given descriptions (all of which have the same parent).
67+
*
68+
* @param siblings unmodifiable collection of descriptions to order
69+
*/
70+
public abstract List<Description> order(Collection<Description> siblings);
71+
}

src/main/java/org/junit/runner/manipulation/Sortable.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,6 @@ public interface Sortable {
1515
*
1616
* @param sorter the {@link Sorter} to use for sorting the tests
1717
*/
18-
public void sort(Sorter sorter);
18+
void sort(Sorter sorter);
1919

2020
}

src/main/java/org/junit/runner/manipulation/Sorter.java

+25-5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
package org.junit.runner.manipulation;
22

3+
import java.util.ArrayList;
4+
import java.util.Collection;
5+
import java.util.Collections;
36
import java.util.Comparator;
7+
import java.util.List;
48

59
import org.junit.runner.Description;
610

@@ -10,7 +14,7 @@
1014
*
1115
* @since 4.0
1216
*/
13-
public class Sorter implements Comparator<Description> {
17+
public class Sorter extends Ordering implements Comparator<Description> {
1418
/**
1519
* NULL is a <code>Sorter</code> that leaves elements in an undefined order
1620
*/
@@ -33,16 +37,32 @@ public Sorter(Comparator<Description> comparator) {
3337
}
3438

3539
/**
36-
* Sorts the test in <code>runner</code> using <code>comparator</code>
40+
* Sorts the test in <code>runner</code> using <code>comparator</code>.
3741
*/
38-
public void apply(Object object) {
39-
if (object instanceof Sortable) {
40-
Sortable sortable = (Sortable) object;
42+
@Override
43+
public void apply(Object runner) {
44+
// Sorting is more efficient than ordering, so check if the runner is Sortable first
45+
if (runner instanceof Sortable) {
46+
Sortable sortable = (Sortable) runner;
4147
sortable.sort(this);
48+
} else {
49+
try {
50+
super.apply(runner);
51+
} catch (InvalidOrderingException e) {
52+
// Can never get here when applying a sortable ordering.
53+
throw new AssertionError(e);
54+
}
4255
}
4356
}
4457

4558
public int compare(Description o1, Description o2) {
4659
return comparator.compare(o1, o2);
4760
}
61+
62+
@Override
63+
public final List<Description> order(Collection<Description> siblings) {
64+
List<Description> sorted = new ArrayList<Description>(siblings);
65+
Collections.sort(sorted, this);
66+
return sorted;
67+
}
4868
}

0 commit comments

Comments
 (0)