Removes duplicate method dispatchers generated by GWTTestCase.

This also adds test cases to verify GWTTestCase correctly handles inheritance.

Change-Id: I52fe1d3ee60ec4c289e4055653125253c5639e76
Review-Link: https://gwt-review.googlesource.com/#/c/2590/

Review by: mdempsky@google.com

git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@11608 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/junit/rebind/GWTRunnerProxyGenerator.java b/user/src/com/google/gwt/junit/rebind/GWTRunnerProxyGenerator.java
index dc174d4..36e8c4f 100644
--- a/user/src/com/google/gwt/junit/rebind/GWTRunnerProxyGenerator.java
+++ b/user/src/com/google/gwt/junit/rebind/GWTRunnerProxyGenerator.java
@@ -28,6 +28,7 @@
 import com.google.gwt.core.ext.typeinfo.JType;
 import com.google.gwt.core.ext.typeinfo.NotFoundException;
 import com.google.gwt.core.ext.typeinfo.TypeOracle;
+import com.google.gwt.dev.util.collect.HashMap;
 import com.google.gwt.junit.client.GWTTestCase;
 import com.google.gwt.junit.client.GWTTestCase.TestModuleInfo;
 import com.google.gwt.junit.client.impl.GWTRunnerProxy;
@@ -38,9 +39,7 @@
 import com.google.gwt.user.rebind.SourceWriter;
 
 import java.io.PrintWriter;
-import java.util.ArrayList;
 import java.util.LinkedHashMap;
-import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
@@ -246,16 +245,23 @@
     return composerFactory.createSourceWriter(ctx, printWriter);
   }
 
-  private static List<JMethod> getTestMethods(JClassType requestedClass) {
-    List<JMethod> list = new ArrayList<JMethod>();
+  // This is compatible with how junit3 identifies test methods
+  private static Iterable<JMethod> getTestMethods(JClassType requestedClass) {
+    Map<String, JMethod> methodMap = new HashMap<String, JMethod>();
     for (JClassType cls = requestedClass; cls != null; cls = cls.getSuperclass()) {
       for (JMethod declMethod : cls.getMethods()) {
         if (isJUnitTestMethod(declMethod)) {
-          list.add(declMethod);
+          putIfAbsent(methodMap, declMethod);
         }
       }
     }
-    return list;
+    return methodMap.values();
+  }
+
+  private static void putIfAbsent(Map<String, JMethod> methodMap, JMethod declMethod) {
+    if (!methodMap.containsKey(declMethod.getName())) {
+      methodMap.put(declMethod.getName(), declMethod);
+    }
   }
 
   private static boolean isJUnitTestMethod(JMethod m) {
diff --git a/user/test/com/google/gwt/junit/JUnitSuite.java b/user/test/com/google/gwt/junit/JUnitSuite.java
index 05f9879..46a4bb4 100644
--- a/user/test/com/google/gwt/junit/JUnitSuite.java
+++ b/user/test/com/google/gwt/junit/JUnitSuite.java
@@ -17,6 +17,7 @@
 
 import com.google.gwt.junit.client.DevModeOnCompiledScriptTest;
 import com.google.gwt.junit.client.GWTTestCaseAsyncTest;
+import com.google.gwt.junit.client.GWTTestCaseInheritanceTest;
 import com.google.gwt.junit.client.GWTTestCaseSetupTearDownTest;
 import com.google.gwt.junit.client.GWTTestCaseStackTraceTest;
 import com.google.gwt.junit.client.GWTTestCaseTest;
@@ -36,8 +37,9 @@
     suite.addTestSuite(GWTTestCaseTest.class);
     suite.addTestSuite(GWTTestCaseStackTraceTest.class);
     suite.addTestSuite(GWTTestCaseUncaughtExceptionHandlerTest.class);
-    suite.addTest(new TestSuiteWithOrder(GWTTestCaseAsyncTest.class));
     suite.addTest(new TestSuiteWithOrder(GWTTestCaseSetupTearDownTest.class));
+    suite.addTest(new TestSuiteWithOrder(GWTTestCaseInheritanceTest.class));
+    suite.addTest(new TestSuiteWithOrder(GWTTestCaseAsyncTest.class));
 
     suite.addTestSuite(DevModeOnCompiledScriptTest.class);
 
diff --git a/user/test/com/google/gwt/junit/TestSuiteWithOrder.java b/user/test/com/google/gwt/junit/TestSuiteWithOrder.java
index 86c77fc..c71aaec 100644
--- a/user/test/com/google/gwt/junit/TestSuiteWithOrder.java
+++ b/user/test/com/google/gwt/junit/TestSuiteWithOrder.java
@@ -20,9 +20,12 @@
 import junit.framework.TestSuite;
 
 import java.lang.reflect.Method;
-import java.lang.reflect.Modifier;
-import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.Collections;
 import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
 
 /**
  * Add {@link TestSuite} that runs test cases in alphabetical order.
@@ -35,41 +38,36 @@
    * instantiated. The implementation of this class should guarantee the order not only by sorting
    * TestCases but also creating them in order.
    */
-
   public TestSuiteWithOrder(Class<? extends TestCase> clazz) {
     super(clazz.getName());
+    Map<String, Method> testMethodMap = new HashMap<String, Method>();
     for (Class<?> c = clazz; Test.class.isAssignableFrom(c); c = c.getSuperclass()) {
-      for (Method each : getDeclaredMethods(c)) {
-        if (isTestMethod(each)) {
-          addTestMethod(each, clazz);
+      for (Method method : c.getDeclaredMethods()) {
+        if (isTestMethod(method) && !testMethodMap.containsKey(method.getName())) {
+          testMethodMap.put(method.getName(), method);
         }
       }
     }
+    for (Method m : getSortedTestMethods(testMethodMap)) {
+      addTest(createTest(clazz, m.getName()));
+    }
     if (countTestCases() == 0) {
       addTest(warning("No tests found in " + clazz.getName()));
     }
   }
 
-  private void addTestMethod(Method m, Class<?> theClass) {
-    if (!Modifier.isPublic(m.getModifiers())) {
-      addTest(warning("Test method isn't public: " + m.getName() + "(" + theClass.getName() + ")"));
-      return;
-    }
-    addTest(createTest(theClass, m.getName()));
-  }
-
   private boolean isTestMethod(Method m) {
     return m.getParameterTypes().length == 0
         && m.getName().startsWith("test")
         && m.getReturnType().equals(Void.TYPE);
   }
 
-  private Method[] getDeclaredMethods(Class<?> clazz) {
-    Method[] methods = clazz.getDeclaredMethods();
-    Arrays.sort(methods, new Comparator<Method>() {
+  private List<Method> getSortedTestMethods(Map<String, Method> methodNames) {
+    List<Method> methods = new ArrayList<Method>(methodNames.values());
+    Collections.sort(methods, new Comparator<Method>() {
       @Override
       public int compare(Method m1, Method m2) {
-        return m1.toString().compareTo(m2.toString());
+        return m1.getName().toString().compareTo(m2.getName().toString());
       }
     });
     return methods;
diff --git a/user/test/com/google/gwt/junit/client/GWTTestCaseInheritanceTest.java b/user/test/com/google/gwt/junit/client/GWTTestCaseInheritanceTest.java
new file mode 100644
index 0000000..5ad6060
--- /dev/null
+++ b/user/test/com/google/gwt/junit/client/GWTTestCaseInheritanceTest.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.junit.client;
+
+
+import static java.util.Arrays.asList;
+
+/**
+ * This class tests inherited tests are executed conforming to junit3 rules.
+ *
+ * Note: This test requires some test methods to be executed in a specific order.
+ */
+public class GWTTestCaseInheritanceTest extends GWTTestCaseInheritanceTestBase {
+
+  @Override
+  protected void gwtTearDown() throws Exception {
+    executions.add(getName());
+  }
+
+  @ExpectedFailure
+  @Override
+  public void testOverridden() {
+    fail("failed on purpose");
+  }
+
+  /**
+   * This is the last test to be executed (under_score forces that). Will assert all test runs.
+   */
+  public void test_assertExecution() {
+    assertEquals(asList("testBasic", "testOverridden", "testStatic"), executions);
+  }
+}
diff --git a/user/test/com/google/gwt/junit/client/GWTTestCaseInheritanceTestBase.java b/user/test/com/google/gwt/junit/client/GWTTestCaseInheritanceTestBase.java
new file mode 100644
index 0000000..437bdd7
--- /dev/null
+++ b/user/test/com/google/gwt/junit/client/GWTTestCaseInheritanceTestBase.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.junit.client;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A test class that is inherited by {@link GWTTestCaseInheritanceTest}.
+ */
+class GWTTestCaseInheritanceTestBase extends GWTTestCaseTestBase {
+
+  static List<String> executions = new ArrayList<String>();
+
+  public void testBasic() { }
+
+  public void testOverridden() { }
+
+  public static void testStatic() { }
+}