From 3448af3ea34963ae8f6481e8b16a2a1b5de605bb Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Fri, 26 Aug 2011 12:47:17 -0400 Subject: [PATCH 1/7] Should explicitly specify source=1.5. JDK 7 javac warns if you do not; Ant inserts it for you but warns. --- build.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/build.xml b/build.xml index d6c0482733e6..6cf3436a1ee1 100644 --- a/build.xml +++ b/build.xml @@ -67,6 +67,7 @@ debug="on" classpath="@{classpath}" includeantruntime="false" + source="1.5" target="1.5" > From cb69050a45f1688ad8c6e817cc511cfa67401f63 Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Fri, 26 Aug 2011 13:13:27 -0400 Subject: [PATCH 2/7] Preditably sort (test) methods in a class. The JVM does not guarantee any order. Visible here as an occasional failure in ParentRunnerTest.useChildHarvester on JDK 7 prior to fix. --- src/main/java/junit/framework/TestSuite.java | 3 +- .../java/org/junit/internal/MethodSorter.java | 35 +++++++++++++++++++ .../org/junit/internal/runners/TestClass.java | 3 +- .../org/junit/runners/model/TestClass.java | 3 +- 4 files changed, 41 insertions(+), 3 deletions(-) create mode 100644 src/main/java/org/junit/internal/MethodSorter.java diff --git a/src/main/java/junit/framework/TestSuite.java b/src/main/java/junit/framework/TestSuite.java index 336efd1800d7..b67006af2e63 100644 --- a/src/main/java/junit/framework/TestSuite.java +++ b/src/main/java/junit/framework/TestSuite.java @@ -10,6 +10,7 @@ import java.util.Enumeration; import java.util.List; import java.util.Vector; +import org.junit.internal.MethodSorter; /** *

A TestSuite is a Composite of Tests. @@ -146,7 +147,7 @@ private void addTestsFromTestCase(final Class theClass) { Class superClass= theClass; List names= new ArrayList(); while (Test.class.isAssignableFrom(superClass)) { - for (Method each : superClass.getDeclaredMethods()) + for (Method each : MethodSorter.getDeclaredMethods(superClass)) addTestMethod(each, names, theClass); superClass= superClass.getSuperclass(); } diff --git a/src/main/java/org/junit/internal/MethodSorter.java b/src/main/java/org/junit/internal/MethodSorter.java new file mode 100644 index 000000000000..844e1c6f95cd --- /dev/null +++ b/src/main/java/org/junit/internal/MethodSorter.java @@ -0,0 +1,35 @@ +package org.junit.internal; + +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.Comparator; + +public class MethodSorter { + + /** + * Gets declared methods of a class in a predictable order. + * Using the "natural" 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. + * @param clazz a class + * @return same as {@link Class#getDeclaredMethods} but sorted + * @see JDK + * (non-)bug #7023180 + */ + public static Method[] getDeclaredMethods(Class clazz) { + Method[] methods = clazz.getDeclaredMethods(); + Arrays.sort(methods, new Comparator() { + @Override public int compare(Method m1, Method m2) { + // Alpha sort by name, and secondarily by other differentiating + // information (parameters and return type). + return m1.toString().compareTo(m2.toString()); + } + }); + return methods; + } + + private MethodSorter() {} + +} diff --git a/src/main/java/org/junit/internal/runners/TestClass.java b/src/main/java/org/junit/internal/runners/TestClass.java index 1ca2b9d1d952..69f404fcf59e 100644 --- a/src/main/java/org/junit/internal/runners/TestClass.java +++ b/src/main/java/org/junit/internal/runners/TestClass.java @@ -11,6 +11,7 @@ import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; +import org.junit.internal.MethodSorter; import org.junit.runners.BlockJUnit4ClassRunner; /** @@ -41,7 +42,7 @@ List getAfters() { public List getAnnotatedMethods(Class annotationClass) { List results= new ArrayList(); for (Class eachClass : getSuperClasses(fClass)) { - Method[] methods= eachClass.getDeclaredMethods(); + Method[] methods= MethodSorter.getDeclaredMethods(eachClass); for (Method eachMethod : methods) { Annotation annotation= eachMethod.getAnnotation(annotationClass); if (annotation != null && ! isShadowed(eachMethod, results)) diff --git a/src/main/java/org/junit/runners/model/TestClass.java b/src/main/java/org/junit/runners/model/TestClass.java index 891059ab821e..708f9c5a4e39 100644 --- a/src/main/java/org/junit/runners/model/TestClass.java +++ b/src/main/java/org/junit/runners/model/TestClass.java @@ -12,6 +12,7 @@ import org.junit.Assert; import org.junit.Before; import org.junit.BeforeClass; +import org.junit.internal.MethodSorter; /** * Wraps a class to be run, providing method validation and annotation searching @@ -36,7 +37,7 @@ public TestClass(Class klass) { "Test class can only have one constructor"); for (Class eachClass : getSuperClasses(fClass)) { - for (Method eachMethod : eachClass.getDeclaredMethods()) + for (Method eachMethod : MethodSorter.getDeclaredMethods(eachClass)) addToAnnotationLists(new FrameworkMethod(eachMethod), fMethodsForAnnotations); for (Field eachField : eachClass.getDeclaredFields()) From b40493d53dab022c8a58c80284c01605b71d1d92 Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Mon, 19 Sep 2011 17:56:05 -0400 Subject: [PATCH 3/7] Use bytecode order of methods when possible. --- .../java/org/junit/internal/MethodSorter.java | 150 +++++++++++++++++- .../org/junit/internal/MethodSorterTest.java | 44 +++++ 2 files changed, 189 insertions(+), 5 deletions(-) create mode 100644 src/test/java/org/junit/internal/MethodSorterTest.java diff --git a/src/main/java/org/junit/internal/MethodSorter.java b/src/main/java/org/junit/internal/MethodSorter.java index 844e1c6f95cd..a9e2ce686672 100644 --- a/src/main/java/org/junit/internal/MethodSorter.java +++ b/src/main/java/org/junit/internal/MethodSorter.java @@ -1,35 +1,175 @@ package org.junit.internal; +import java.io.DataInput; +import java.io.DataInputStream; +import java.io.IOException; +import java.io.InputStream; import java.lang.reflect.Method; +import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; +import java.util.List; public class MethodSorter { /** * Gets declared methods of a class in a predictable order. - * Using the "natural" order is unwise since the Java platform does not + * 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. + * certain platforms. Uses bytecode order if available, else lexicographic. * @param clazz a class * @return same as {@link Class#getDeclaredMethods} but sorted * @see JDK * (non-)bug #7023180 */ public static Method[] getDeclaredMethods(Class clazz) { + final List names; Method[] methods = clazz.getDeclaredMethods(); + try { + names = methodNamesAndDescriptors(clazz); + } catch (IOException x) { + // TODO report somehow? + return methods; + } Arrays.sort(methods, new Comparator() { @Override public int compare(Method m1, Method m2) { - // Alpha sort by name, and secondarily by other differentiating - // information (parameters and return type). - return m1.toString().compareTo(m2.toString()); + int i1 = names.indexOf(nameAndDescriptor(m1)); + int i2 = names.indexOf(nameAndDescriptor(m2)); + return i1 != i2 ? i1 - i2 : m1.toString().compareTo(m2.toString()); } }); return methods; } + static List methodNamesAndDescriptors(Class clazz) throws IOException { + ClassLoader l = clazz.getClassLoader(); + String name = clazz.getName().replace('.', '/') + ".class"; + InputStream is = l != null ? l.getResourceAsStream(name) : ClassLoader.getSystemResourceAsStream(name); + try { + return methodNamesAndDescriptors(is); + } finally { + is.close(); + } + } + + private static List methodNamesAndDescriptors(InputStream bytecode) throws IOException { + DataInput input = new DataInputStream(bytecode); + skip(input, 8); // magic, minor_version, major_version + int size = input.readUnsignedShort() - 1; // constant_pool_count + String[] utf8Strings = new String[size]; + for (int i = 0; i < size; i++) { // constant_pool + byte tag = input.readByte(); + switch (tag) { + case 1: // CONSTANT_Utf8 + utf8Strings[i] = input.readUTF(); + break; + case 7: // CONSTANT_Class + case 8: // CONSTANT_String + skip(input, 2); + break; + case 3: // CONSTANT_Integer + case 4: // CONSTANT_Float + case 9: // CONSTANT_Fieldref + case 10: // CONSTANT_Methodref + case 11: // CONSTANT_InterfaceMethodref + case 12: // CONSTANT_NameAndType + skip(input, 4); + break; + case 5: // CONSTANT_Long + case 6: // CONSTANT_Double + skip(input, 8); + i++; // weirdness in spec + break; + default: + throw new IOException("Unrecognized constant pool tag " + tag + " @" + i + " after " + Arrays.asList(utf8Strings)); + } + } + skip(input, 6); // access_flags, this_class, super_class + skip(input, input.readUnsignedShort() * 2); // interfaces_count, interfaces + int fields_count = input.readUnsignedShort(); + for (int i = 0; i < fields_count; i++) { // field_info + skip(input, 6); // access_flags, name_index, descriptor_index + int attributes_count = input.readUnsignedShort(); + for (int j = 0; j < attributes_count; j++) { // attribute_info + skip(input, 2); //attribute_name_index + skip(input, input.readInt()); // attribute_length, info + } + } + int methods_count = input.readUnsignedShort(); + List result = new ArrayList(methods_count); + for (int i = 0; i < methods_count; i++) { // method_info + skip(input, 2); // access_flags + int name_index = input.readUnsignedShort() - 1; + if (name_index >= size || name_index < 0) { + throw new IOException("@" + i + ": method_info.name_index " + name_index + " vs. size of pool " + size); + } + int descriptor_index = input.readUnsignedShort() - 1; + if (descriptor_index >= size || descriptor_index < 0) { + throw new IOException("@" + i + ": method_info.descriptor_index " + descriptor_index + " vs. size of pool " + size); + } + String name = utf8Strings[name_index]; + if (!name.equals("") && !name.equals("")) { + result.add(name + '~' + utf8Strings[descriptor_index]); + } + int attributes_count = input.readUnsignedShort(); + for (int j = 0; j < attributes_count; j++) { // attribute_info + skip(input, 2); //attribute_name_index + skip(input, input.readInt()); // attribute_length, info + } + } + return result; + } + + private static void skip(DataInput input, int bytes) throws IOException { + int skipped = input.skipBytes(bytes); + if (skipped != bytes) { + throw new IOException("Truncated class file"); + } + } + + static String nameAndDescriptor(Method m) { + StringBuilder b = new StringBuilder(m.getName()); + b.append('~'); + b.append('('); + for (Class c : m.getParameterTypes()) { + binaryName(c, b); + } + b.append(')'); + binaryName(m.getReturnType(), b); + return b.toString(); + } + + private static void binaryName(Class c, StringBuilder b) { + if (c == Void.TYPE) { + b.append('V'); + } else if (c == Byte.TYPE) { + b.append('B'); + } else if (c == Character.TYPE) { + b.append('C'); + } else if (c == Double.TYPE) { + b.append('D'); + } else if (c == Float.TYPE) { + b.append('F'); + } else if (c == Integer.TYPE) { + b.append('I'); + } else if (c == Long.TYPE) { + b.append('J'); + } else if (c == Short.TYPE) { + b.append('S'); + } else if (c == Boolean.TYPE) { + b.append('Z'); + } else if (c.isArray()) { + b.append('['); + binaryName(c.getComponentType(), b); + } else { + b.append('L'); + b.append(c.getName().replace('.', '/')); + b.append(';'); + } + } + private MethodSorter() {} } diff --git a/src/test/java/org/junit/internal/MethodSorterTest.java b/src/test/java/org/junit/internal/MethodSorterTest.java new file mode 100644 index 000000000000..b09932fb1afc --- /dev/null +++ b/src/test/java/org/junit/internal/MethodSorterTest.java @@ -0,0 +1,44 @@ +package org.junit.internal; + +import java.util.Arrays; +import org.junit.Test; +import static org.junit.Assert.*; + +public class MethodSorterTest { + + @Test public void getDeclaredMethods() throws Exception { + assertEquals("[java.lang.Object a(int,double,java.lang.Thread), void b(int[][]), int c(), void c(boolean)]", declaredMethods(Dummy.class)); + class Super { + void testOne() {} + } + class Sub extends Super { + void testTwo() {} + } + 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() + '.', ""); + } + + @Test public void methodNamesAndDescriptors() throws Exception { + assertEquals("[a~(IDLjava/lang/Thread;)Ljava/lang/Object;, b~([[I)V, c~()I, c~(Z)V]", + MethodSorter.methodNamesAndDescriptors(Dummy.class).toString()); + } + + @Test public void nameAndDescriptor() throws Exception { + assertEquals("a~(IDLjava/lang/Thread;)Ljava/lang/Object;", MethodSorter.nameAndDescriptor(Dummy.class.getDeclaredMethod("a", int.class, double.class, Thread.class))); + assertEquals("b~([[I)V", MethodSorter.nameAndDescriptor(Dummy.class.getDeclaredMethod("b", int[][].class))); + assertEquals("c~()I", MethodSorter.nameAndDescriptor(Dummy.class.getDeclaredMethod("c"))); + assertEquals("c~(Z)V", MethodSorter.nameAndDescriptor(Dummy.class.getDeclaredMethod("c", boolean.class))); + } + + private static class Dummy { + Object a(int i, double d, Thread t) {return null;} + void b(int[][] x) {} + int c() {return 0;} + void c(boolean b) {} + } + +} From 785a33b478d358e79a3b6592754ad9ec051e4278 Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Mon, 19 Sep 2011 18:01:09 -0400 Subject: [PATCH 4/7] If bytecode cannot be located or parsed, at least alpha sort. --- src/main/java/org/junit/internal/MethodSorter.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/junit/internal/MethodSorter.java b/src/main/java/org/junit/internal/MethodSorter.java index a9e2ce686672..11fc3687bcad 100644 --- a/src/main/java/org/junit/internal/MethodSorter.java +++ b/src/main/java/org/junit/internal/MethodSorter.java @@ -7,6 +7,7 @@ import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.Comparator; import java.util.List; @@ -25,18 +26,18 @@ public class MethodSorter { * (non-)bug #7023180 */ public static Method[] getDeclaredMethods(Class clazz) { - final List names; + List names = Collections.emptyList(); Method[] methods = clazz.getDeclaredMethods(); try { names = methodNamesAndDescriptors(clazz); } catch (IOException x) { // TODO report somehow? - return methods; } + final List _names = names; Arrays.sort(methods, new Comparator() { @Override public int compare(Method m1, Method m2) { - int i1 = names.indexOf(nameAndDescriptor(m1)); - int i2 = names.indexOf(nameAndDescriptor(m2)); + int i1 = _names.indexOf(nameAndDescriptor(m1)); + int i2 = _names.indexOf(nameAndDescriptor(m2)); return i1 != i2 ? i1 - i2 : m1.toString().compareTo(m2.toString()); } }); From 5d9e0022d3f6db1367d530579de6332b9c8802e3 Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Wed, 21 Sep 2011 17:48:06 -0400 Subject: [PATCH 5/7] Switching to method sort based on hash code - deterministic but hard to abuse. --- .../java/org/junit/internal/MethodSorter.java | 147 +----------------- .../org/junit/internal/MethodSorterTest.java | 24 +-- .../running/classes/ParentRunnerTest.java | 2 +- 3 files changed, 11 insertions(+), 162 deletions(-) diff --git a/src/main/java/org/junit/internal/MethodSorter.java b/src/main/java/org/junit/internal/MethodSorter.java index 11fc3687bcad..4703edf36f38 100644 --- a/src/main/java/org/junit/internal/MethodSorter.java +++ b/src/main/java/org/junit/internal/MethodSorter.java @@ -1,15 +1,8 @@ package org.junit.internal; -import java.io.DataInput; -import java.io.DataInputStream; -import java.io.IOException; -import java.io.InputStream; import java.lang.reflect.Method; -import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; import java.util.Comparator; -import java.util.List; public class MethodSorter { @@ -19,158 +12,24 @@ public class MethodSorter { * 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 bytecode order if available, else lexicographic. + * certain platforms. Uses an unspecified but deterministic order. * @param clazz a class * @return same as {@link Class#getDeclaredMethods} but sorted * @see JDK * (non-)bug #7023180 */ public static Method[] getDeclaredMethods(Class clazz) { - List names = Collections.emptyList(); Method[] methods = clazz.getDeclaredMethods(); - try { - names = methodNamesAndDescriptors(clazz); - } catch (IOException x) { - // TODO report somehow? - } - final List _names = names; Arrays.sort(methods, new Comparator() { @Override public int compare(Method m1, Method m2) { - int i1 = _names.indexOf(nameAndDescriptor(m1)); - int i2 = _names.indexOf(nameAndDescriptor(m2)); + int i1 = m1.getName().hashCode(); + int i2 = m2.getName().hashCode(); return i1 != i2 ? i1 - i2 : m1.toString().compareTo(m2.toString()); } }); return methods; } - static List methodNamesAndDescriptors(Class clazz) throws IOException { - ClassLoader l = clazz.getClassLoader(); - String name = clazz.getName().replace('.', '/') + ".class"; - InputStream is = l != null ? l.getResourceAsStream(name) : ClassLoader.getSystemResourceAsStream(name); - try { - return methodNamesAndDescriptors(is); - } finally { - is.close(); - } - } - - private static List methodNamesAndDescriptors(InputStream bytecode) throws IOException { - DataInput input = new DataInputStream(bytecode); - skip(input, 8); // magic, minor_version, major_version - int size = input.readUnsignedShort() - 1; // constant_pool_count - String[] utf8Strings = new String[size]; - for (int i = 0; i < size; i++) { // constant_pool - byte tag = input.readByte(); - switch (tag) { - case 1: // CONSTANT_Utf8 - utf8Strings[i] = input.readUTF(); - break; - case 7: // CONSTANT_Class - case 8: // CONSTANT_String - skip(input, 2); - break; - case 3: // CONSTANT_Integer - case 4: // CONSTANT_Float - case 9: // CONSTANT_Fieldref - case 10: // CONSTANT_Methodref - case 11: // CONSTANT_InterfaceMethodref - case 12: // CONSTANT_NameAndType - skip(input, 4); - break; - case 5: // CONSTANT_Long - case 6: // CONSTANT_Double - skip(input, 8); - i++; // weirdness in spec - break; - default: - throw new IOException("Unrecognized constant pool tag " + tag + " @" + i + " after " + Arrays.asList(utf8Strings)); - } - } - skip(input, 6); // access_flags, this_class, super_class - skip(input, input.readUnsignedShort() * 2); // interfaces_count, interfaces - int fields_count = input.readUnsignedShort(); - for (int i = 0; i < fields_count; i++) { // field_info - skip(input, 6); // access_flags, name_index, descriptor_index - int attributes_count = input.readUnsignedShort(); - for (int j = 0; j < attributes_count; j++) { // attribute_info - skip(input, 2); //attribute_name_index - skip(input, input.readInt()); // attribute_length, info - } - } - int methods_count = input.readUnsignedShort(); - List result = new ArrayList(methods_count); - for (int i = 0; i < methods_count; i++) { // method_info - skip(input, 2); // access_flags - int name_index = input.readUnsignedShort() - 1; - if (name_index >= size || name_index < 0) { - throw new IOException("@" + i + ": method_info.name_index " + name_index + " vs. size of pool " + size); - } - int descriptor_index = input.readUnsignedShort() - 1; - if (descriptor_index >= size || descriptor_index < 0) { - throw new IOException("@" + i + ": method_info.descriptor_index " + descriptor_index + " vs. size of pool " + size); - } - String name = utf8Strings[name_index]; - if (!name.equals("") && !name.equals("")) { - result.add(name + '~' + utf8Strings[descriptor_index]); - } - int attributes_count = input.readUnsignedShort(); - for (int j = 0; j < attributes_count; j++) { // attribute_info - skip(input, 2); //attribute_name_index - skip(input, input.readInt()); // attribute_length, info - } - } - return result; - } - - private static void skip(DataInput input, int bytes) throws IOException { - int skipped = input.skipBytes(bytes); - if (skipped != bytes) { - throw new IOException("Truncated class file"); - } - } - - static String nameAndDescriptor(Method m) { - StringBuilder b = new StringBuilder(m.getName()); - b.append('~'); - b.append('('); - for (Class c : m.getParameterTypes()) { - binaryName(c, b); - } - b.append(')'); - binaryName(m.getReturnType(), b); - return b.toString(); - } - - private static void binaryName(Class c, StringBuilder b) { - if (c == Void.TYPE) { - b.append('V'); - } else if (c == Byte.TYPE) { - b.append('B'); - } else if (c == Character.TYPE) { - b.append('C'); - } else if (c == Double.TYPE) { - b.append('D'); - } else if (c == Float.TYPE) { - b.append('F'); - } else if (c == Integer.TYPE) { - b.append('I'); - } else if (c == Long.TYPE) { - b.append('J'); - } else if (c == Short.TYPE) { - b.append('S'); - } else if (c == Boolean.TYPE) { - b.append('Z'); - } else if (c.isArray()) { - b.append('['); - binaryName(c.getComponentType(), b); - } else { - b.append('L'); - b.append(c.getName().replace('.', '/')); - b.append(';'); - } - } - private MethodSorter() {} } diff --git a/src/test/java/org/junit/internal/MethodSorterTest.java b/src/test/java/org/junit/internal/MethodSorterTest.java index b09932fb1afc..f679c33533bf 100644 --- a/src/test/java/org/junit/internal/MethodSorterTest.java +++ b/src/test/java/org/junit/internal/MethodSorterTest.java @@ -7,7 +7,7 @@ public class MethodSorterTest { @Test public void getDeclaredMethods() throws Exception { - assertEquals("[java.lang.Object a(int,double,java.lang.Thread), void b(int[][]), int c(), void c(boolean)]", declaredMethods(Dummy.class)); + 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)); class Super { void testOne() {} } @@ -22,23 +22,13 @@ private static String declaredMethods(Class c) { return Arrays.toString(MethodSorter.getDeclaredMethods(c)).replace(c.getName() + '.', ""); } - @Test public void methodNamesAndDescriptors() throws Exception { - assertEquals("[a~(IDLjava/lang/Thread;)Ljava/lang/Object;, b~([[I)V, c~()I, c~(Z)V]", - MethodSorter.methodNamesAndDescriptors(Dummy.class).toString()); - } - - @Test public void nameAndDescriptor() throws Exception { - assertEquals("a~(IDLjava/lang/Thread;)Ljava/lang/Object;", MethodSorter.nameAndDescriptor(Dummy.class.getDeclaredMethod("a", int.class, double.class, Thread.class))); - assertEquals("b~([[I)V", MethodSorter.nameAndDescriptor(Dummy.class.getDeclaredMethod("b", int[][].class))); - assertEquals("c~()I", MethodSorter.nameAndDescriptor(Dummy.class.getDeclaredMethod("c"))); - assertEquals("c~(Z)V", MethodSorter.nameAndDescriptor(Dummy.class.getDeclaredMethod("c", boolean.class))); - } - private static class Dummy { - Object a(int i, double d, Thread t) {return null;} - void b(int[][] x) {} - int c() {return 0;} - void c(boolean b) {} + 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() {} } } diff --git a/src/test/java/org/junit/tests/running/classes/ParentRunnerTest.java b/src/test/java/org/junit/tests/running/classes/ParentRunnerTest.java index 21c22b82c639..d7ac9bd83e57 100644 --- a/src/test/java/org/junit/tests/running/classes/ParentRunnerTest.java +++ b/src/test/java/org/junit/tests/running/classes/ParentRunnerTest.java @@ -33,7 +33,7 @@ public void apple() { } @Test - public void banana() { + public void /* must hash-sort after "apple" */Banana() { log+= "banana "; } } From 3920f3fe11dffc904e1ab41a9ff4fc9d36b1c25b Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Tue, 31 Jan 2012 14:52:41 -0500 Subject: [PATCH 6/7] Making Super and Sub member classes as suggested. https://github.com/KentBeck/junit/pull/293/files#r403246 --- .../java/org/junit/internal/MethodSorterTest.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/test/java/org/junit/internal/MethodSorterTest.java b/src/test/java/org/junit/internal/MethodSorterTest.java index f679c33533bf..737c745fc08a 100644 --- a/src/test/java/org/junit/internal/MethodSorterTest.java +++ b/src/test/java/org/junit/internal/MethodSorterTest.java @@ -8,12 +8,6 @@ public class MethodSorterTest { @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)); - class Super { - void testOne() {} - } - class Sub extends Super { - void testTwo() {} - } assertEquals("[void testOne()]", declaredMethods(Super.class)); assertEquals("[void testTwo()]", declaredMethods(Sub.class)); } @@ -30,5 +24,11 @@ void gamma(boolean b) {} void delta() {} void epsilon() {} } + private static class Super { + void testOne() {} + } + private static class Sub extends Super { + void testTwo() {} + } } From 5f2ecdacca8db679081ff6dede52cb0d8970d730 Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Tue, 31 Jan 2012 14:54:56 -0500 Subject: [PATCH 7/7] Using MethodSorter here too, just in case it matters. --- src/main/java/org/junit/internal/matchers/TypeSafeMatcher.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/junit/internal/matchers/TypeSafeMatcher.java b/src/main/java/org/junit/internal/matchers/TypeSafeMatcher.java index 794a1749d9c9..bccbdc72d371 100644 --- a/src/main/java/org/junit/internal/matchers/TypeSafeMatcher.java +++ b/src/main/java/org/junit/internal/matchers/TypeSafeMatcher.java @@ -3,6 +3,7 @@ import java.lang.reflect.Method; import org.hamcrest.BaseMatcher; +import org.junit.internal.MethodSorter; /** * Convenient base class for Matchers that require a non-null value of a specific type. @@ -26,7 +27,7 @@ protected TypeSafeMatcher() { private static Class findExpectedType(Class fromClass) { for (Class c = fromClass; c != Object.class; c = c.getSuperclass()) { - for (Method method : c.getDeclaredMethods()) { + for (Method method : MethodSorter.getDeclaredMethods(c)) { if (isMatchesSafelyMethod(method)) { return method.getParameterTypes()[0]; }