Skip to content

Commit

Permalink
Merge pull request #386 from matthewfarwell/sortwith
Browse files Browse the repository at this point in the history
SortMethodsWith allows the user to choose the order of execution of the methods within a test class
  • Loading branch information
dsaff committed Apr 9, 2012
2 parents 3639784 + 7924760 commit 560322d
Show file tree
Hide file tree
Showing 5 changed files with 186 additions and 21 deletions.
41 changes: 41 additions & 0 deletions src/main/java/org/junit/FixMethodOrder.java
@@ -0,0 +1,41 @@
package org.junit;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import org.junit.runners.MethodSorters;

/**
* This class allows the user to choose the order of execution of the methods within a test class.
* <br/>
* <br/>
* The default order of execution of JUnit tests within a class is deterministic but not predictable.
* The order of execution is not guaranteed for Java 7 (and some previous versions), and can even change
* from run to run, so the order of execution was changed to be deterministic (in JUnit 4.11)
* <br/>
* It is recommended that test methods be written so that they are independent of the order that they are executed.
* However, there may be a number of dependent tests either through error or by design.
* This class allows the user to specify the order of execution of test methods.
* <br/>
* For possibilities, see {@link MethodSorters}
*
* Here is an example:
*
* <pre>
* &#064;FixMethodOrder(MethodSorters.NAME_ASCENDING)
* public class MyTest {
* }
* </pre>
*
* @see org.junit.runners.MethodSorters
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface FixMethodOrder {
/**
* Optionally specify <code>value</code> to have the methods executed in a particular order
*/
MethodSorters value() default MethodSorters.DEFAULT;
}
52 changes: 42 additions & 10 deletions src/main/java/org/junit/internal/MethodSorter.java
Expand Up @@ -4,32 +4,64 @@
import java.util.Arrays;
import java.util.Comparator;

import org.junit.FixMethodOrder;

public class MethodSorter {
/**
* DEFAULT sort order
*/
public static Comparator<Method> DEFAULT= new Comparator<Method>() {
public int compare(Method m1, Method m2) {
int i1 = m1.getName().hashCode();
int i2 = m2.getName().hashCode();
return i1 != i2 ? i1 - i2 : MethodSorter.compare(m1.toString(), m2.toString());
}
};

/**
* Method name ascending lexicograhic sort order
*/
public static Comparator<Method> NAME_ASCENDING= new Comparator<Method>() {
public int compare(Method m1, Method m2) {
return MethodSorter.compare(m1.getName(), m2.getName());
}
};

private static int compare(String s1, String s2) {
return s1.compareTo(s2);
}

/**
* Gets declared methods of a class in a predictable order.
* Gets declared methods of a class in a predictable order, unless @FixMethodOrder(MethodSorters.JVM) is specified.
*
* Using the JVM order is unwise since the Java platform does not
* specify any particular order, and in fact JDK 7 returns a more or less
* random order; well-written test code would not assume any order, but some
* does, and a predictable failure is better than a random failure on
* certain platforms. Uses an unspecified but deterministic order.
* certain platforms. By default, uses an unspecified but deterministic order.
* @param clazz a class
* @return same as {@link Class#getDeclaredMethods} but sorted
* @see <a href="http://bugs.sun.com/view_bug.do?bug_id=7023180">JDK
* (non-)bug #7023180</a>
*/
public static Method[] getDeclaredMethods(Class<?> clazz) {
Method[] methods = clazz.getDeclaredMethods();
Arrays.sort(methods, new Comparator<Method>() {
@Override public int compare(Method m1, Method m2) {
int i1 = m1.getName().hashCode();
int i2 = m2.getName().hashCode();
return i1 != i2 ? i1 - i2 : m1.toString().compareTo(m2.toString());
}
});
Comparator<Method> comparator= getSorter(clazz.getAnnotation(FixMethodOrder.class));

Method[] methods= clazz.getDeclaredMethods();
if (comparator != null) {
Arrays.sort(methods, comparator);
}

return methods;
}

private MethodSorter() {}

private static Comparator<Method> getSorter(FixMethodOrder fixMethodOrder) {
if (fixMethodOrder == null) {
return DEFAULT;
}

return fixMethodOrder.value().getComparator();
}
}
29 changes: 29 additions & 0 deletions src/main/java/org/junit/runners/MethodSorters.java
@@ -0,0 +1,29 @@
package org.junit.runners;

import java.lang.reflect.Method;
import java.util.Comparator;

import org.junit.internal.MethodSorter;

/**
* Sort the methods into a specified execution order.
* Defines common {@link MethodSorter} implementations.
*/
public enum MethodSorters {
/** Sorts the test methods by the method name, in lexicographic order */
NAME_ASCENDING(MethodSorter.NAME_ASCENDING),
/** Sorts the test methods by the method name, in reverse lexicographic order */
JVM(null),
/** the default value, deterministic, but not predictable */
DEFAULT(MethodSorter.DEFAULT);

private final Comparator<Method> fComparator;

private MethodSorters(Comparator<Method> comparator) {
this.fComparator= comparator;
}

public Comparator<Method> getComparator() {
return fComparator;
}
}
81 changes: 71 additions & 10 deletions src/test/java/org/junit/internal/MethodSorterTest.java
@@ -1,34 +1,95 @@
package org.junit.internal;

import java.lang.reflect.Method;
import java.util.Arrays;

import org.junit.FixMethodOrder;
import org.junit.Test;
import org.junit.runners.MethodSorters;
import static org.junit.Assert.*;

public class MethodSorterTest {
private static final String ALPHA= "java.lang.Object alpha(int,double,java.lang.Thread)";
private static final String BETA= "void beta(int[][])";
private static final String GAMMA_VOID= "int gamma()";
private static final String GAMMA_BOOLEAN= "void gamma(boolean)";
private static final String DELTA= "void delta()";
private static final String EPSILON= "void epsilon()";

private static class Dummy {
Object alpha(int i, double d, Thread t) {return null;}
void beta(int[][] x) {}
int gamma() {return 0;}
void gamma(boolean b) {}
void delta() {}
void epsilon() {}
}
private static class Super {
void testOne() {}
}
private static class Sub extends Super {
void testTwo() {}
}

private String toString(Class<?> clazz, Method[] methods) {
return Arrays.toString(methods).replace(clazz.getName() + '.', "");
}

private String declaredMethods(Class<?> clazz) {
return toString(clazz, MethodSorter.getDeclaredMethods(clazz));
}

@Test public void getDeclaredMethods() throws Exception {
assertEquals("[void epsilon(), void beta(int[][]), java.lang.Object alpha(int,double,java.lang.Thread), void delta(), int gamma(), void gamma(boolean)]", declaredMethods(Dummy.class));
@Test public void getMethodsNullSorter() throws Exception {
String[] expected= new String[] { EPSILON, BETA, ALPHA, DELTA, GAMMA_VOID, GAMMA_BOOLEAN };
assertEquals(Arrays.asList(expected).toString(), declaredMethods(Dummy.class));
assertEquals("[void testOne()]", declaredMethods(Super.class));
assertEquals("[void testTwo()]", declaredMethods(Sub.class));
}

private static String declaredMethods(Class<?> c) {
return Arrays.toString(MethodSorter.getDeclaredMethods(c)).replace(c.getName() + '.', "");

@FixMethodOrder(MethodSorters.DEFAULT)
private static class DummySortWithDefault {
Object alpha(int i, double d, Thread t) {return null;}
void beta(int[][] x) {}
int gamma() {return 0;}
void gamma(boolean b) {}
void delta() {}
void epsilon() {}
}

private static class Dummy {
@Test public void testDefaultSorter() {
String[] expected= new String[] { EPSILON, BETA, ALPHA, DELTA, GAMMA_VOID, GAMMA_BOOLEAN };
assertEquals(Arrays.asList(expected).toString(), declaredMethods(DummySortWithDefault.class));
}

@FixMethodOrder(MethodSorters.JVM)
private static class DummySortJvm {
Object alpha(int i, double d, Thread t) {return null;}
void beta(int[][] x) {}
int gamma() {return 0;}
void gamma(boolean b) {}
void delta() {}
void epsilon() {}
}
private static class Super {
void testOne() {}

@Test public void testSortWithJvm() {
Class<?> clazz= DummySortJvm.class;
String actual= toString(clazz, clazz.getDeclaredMethods());

assertEquals(actual, declaredMethods(clazz));
}
private static class Sub extends Super {
void testTwo() {}

@FixMethodOrder(MethodSorters.NAME_ASCENDING)
private static class DummySortWithNameAsc {
Object alpha(int i, double d, Thread t) {return null;}
void beta(int[][] x) {}
int gamma() {return 0;}
void gamma(boolean b) {}
void delta() {}
void epsilon() {}
}

@Test public void testNameAsc() {
String[] expected= new String[] { ALPHA, BETA, DELTA, EPSILON, GAMMA_VOID, GAMMA_BOOLEAN };
assertEquals(Arrays.asList(expected).toString(), declaredMethods(DummySortWithNameAsc.class));
}
}
4 changes: 3 additions & 1 deletion src/test/java/org/junit/tests/AllTests.java
Expand Up @@ -2,6 +2,7 @@

import junit.framework.JUnit4TestAdapter;
import junit.framework.Test;
import org.junit.internal.MethodSorterTest;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
import org.junit.runners.Suite.SuiteClasses;
Expand Down Expand Up @@ -158,7 +159,8 @@
BlockJUnit4ClassRunnerOverrideTest.class,
RuleFieldValidatorTest.class,
RuleChainTest.class,
BlockJUnit4ClassRunnerTest.class
BlockJUnit4ClassRunnerTest.class,
MethodSorterTest.class
})
public class AllTests {
public static Test suite() {
Expand Down

0 comments on commit 560322d

Please sign in to comment.