This patch updates the error reporting inside of JUnitShell to show which remoteweb targets
haven't responded when there is a timeout.

Patch by: zundel
Review by: fabbott(TBR)



git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@2419 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/junit/JUnitMessageQueue.java b/user/src/com/google/gwt/junit/JUnitMessageQueue.java
index 51dfe31..6c80cc8 100644
--- a/user/src/com/google/gwt/junit/JUnitMessageQueue.java
+++ b/user/src/com/google/gwt/junit/JUnitMessageQueue.java
@@ -15,6 +15,7 @@
  */
 package com.google.gwt.junit;
 
+import com.google.gwt.junit.client.TimeoutException;
 import com.google.gwt.junit.client.impl.JUnitResult;
 import com.google.gwt.junit.client.impl.JUnitHost.TestInfo;
 
@@ -23,6 +24,7 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 /**
  * A message queue to pass data between {@link JUnitShell} and {@link
@@ -70,11 +72,6 @@
   private Object resultsLock = new Object();
 
   /**
-   * The name of the module to execute.
-   */
-  private String testModule;
-
-  /**
    * The name of the test class to execute.
    */
   private String testClass;
@@ -85,6 +82,11 @@
   private String testMethod;
 
   /**
+   * The name of the module to execute.
+   */
+  private String testModule;
+
+  /**
    * The results for the current test method.
    */
   private List<JUnitResult> testResult = new ArrayList<JUnitResult>();
@@ -119,6 +121,7 @@
   public TestInfo getNextTestInfo(String clientId, String moduleName,
       long timeout) {
     synchronized (readTestLock) {
+      long startTime = System.currentTimeMillis();
       long stopTime = System.currentTimeMillis() + timeout;
       while (!testIsAvailableFor(clientId, moduleName)) {
         long timeToWait = stopTime - System.currentTimeMillis();
@@ -128,8 +131,12 @@
         try {
           readTestLock.wait(timeToWait);
         } catch (InterruptedException e) {
-          // just abort
-          return null;
+          double elapsed = (System.currentTimeMillis() - startTime) / 1000.0;
+          throw new TimeoutException("The servlet did not respond to the "
+              + "next query to test within "
+              + timeout + "ms.\n" + " Module Name: " + moduleName + "\n"
+              + " Client id: " + clientId + "\n"
+              + " Actual time elapsed: " + elapsed + " seconds.\n");
         }
       }
 
@@ -172,6 +179,44 @@
   }
 
   /**
+   * Returns a pretty printed list of clients that have not retrieved the
+   * current test. Used for error reporting.
+   * 
+   * @return a string containing the list of clients that have not retrieved the
+   *         current test.
+   */
+  String getUnretrievedClients() {
+    int lineCount = 0;
+    StringBuilder buf = new StringBuilder();
+    synchronized (readTestLock) {
+      Set<String> keys = clientTestRequests.keySet();
+
+      for (String key : keys) {
+        if (lineCount > 0) {
+          buf.append('\n');
+        }
+        
+        if (clientTestRequests.get(key) <= currentTestIndex) {
+          buf.append(" - NO RESPONSE: ");
+          buf.append(key);
+        } else {
+          buf.append(" - (ok): ");
+          buf.append(key);
+        }
+        lineCount++;        
+      }
+      int difference = numClients - keys.size();
+      if (difference > 0) {
+        if (lineCount > 0) {
+          buf.append('\n');
+        }
+        buf.append(" - " + difference + " client(s) haven't responded back to the servlet at all since the start of the test.");
+      }
+    }
+    return buf.toString();
+  }
+
+  /**
    * Called by the shell to see if the currently-running test has completed.
    * 
    * @param moduleName the name of the test module
@@ -191,7 +236,7 @@
    */
   boolean haveAllClientsRetrievedCurrentTest() {
     synchronized (readTestLock) {
-      // If a client hasn't yet contacted, it will have no entry
+      // If a client hasn't yet been contacted, it will have no entry
       Collection<Integer> clientIndices = clientTestRequests.values();
       if (clientIndices.size() < numClients) {
         return false;
@@ -210,8 +255,9 @@
    * Called by the shell to set the name of the next method to run for this test
    * class.
    * 
-   * @param testClassName The name of the test class.
-   * @param testName The name of the method to run.
+   * @param testModule the name of the module to be run.
+   * @param testClass The name of the test class.
+   * @param testMethod The name of the method to run.
    */
   void setNextTestName(String testModule, String testClass, String testMethod) {
     synchronized (readTestLock) {
diff --git a/user/src/com/google/gwt/junit/JUnitShell.java b/user/src/com/google/gwt/junit/JUnitShell.java
index 349dc10..d1ef7fb 100644
--- a/user/src/com/google/gwt/junit/JUnitShell.java
+++ b/user/src/com/google/gwt/junit/JUnitShell.java
@@ -165,6 +165,7 @@
         throw new RuntimeException("Invalid shell arguments");
       }
 
+      
       shell.messageQueue = new JUnitMessageQueue(shell.numClients);
 
       if (!shell.startUp()) {
@@ -213,6 +214,11 @@
    * started the test.
    */
   private long testBeginTimeout;
+  
+  /**
+   * The time the test actually began.
+   */
+  private long testBeginTime;
 
   /**
    * Enforce the singleton pattern. The call to {@link GWTShell}'s ctor forces
@@ -350,9 +356,12 @@
   protected boolean notDone() {
     if (!messageQueue.haveAllClientsRetrievedCurrentTest()
         && testBeginTimeout < System.currentTimeMillis()) {
+      double elapsed = (System.currentTimeMillis() - testBeginTime) / 1000.0;
       throw new TimeoutException(
           "The browser did not contact the server within "
-              + TEST_BEGIN_TIMEOUT_MILLIS + "ms.");
+              + TEST_BEGIN_TIMEOUT_MILLIS + "ms.\n"
+              + messageQueue.getUnretrievedClients()
+              + "\n Actual time elapsed: " + elapsed + " seconds.\n");
     }
 
     if (messageQueue.hasResult(currentModule.getName())) {
@@ -426,7 +435,8 @@
     try {
       // Set a timeout period to automatically fail if the servlet hasn't been
       // contacted; something probably went wrong (the module failed to load?)
-      testBeginTimeout = System.currentTimeMillis() + TEST_BEGIN_TIMEOUT_MILLIS;
+      testBeginTime = System.currentTimeMillis();
+      testBeginTimeout = testBeginTime + TEST_BEGIN_TIMEOUT_MILLIS;
       pumpEventLoop();
     } catch (TimeoutException e) {
       lastLaunchFailed = true;
diff --git a/user/src/com/google/gwt/junit/server/JUnitHostImpl.java b/user/src/com/google/gwt/junit/server/JUnitHostImpl.java
index 9c94904..8936cba 100644
--- a/user/src/com/google/gwt/junit/server/JUnitHostImpl.java
+++ b/user/src/com/google/gwt/junit/server/JUnitHostImpl.java
@@ -222,9 +222,10 @@
    */
   private String getClientId() {
     HttpServletRequest request = getThreadLocalRequest();
-    String agent = request.getHeader("User-Agent");
     String machine = request.getRemoteHost();
-    return machine + " / " + agent;
+    String servletPath = request.getServletPath();
+    String agent = request.getHeader("User-Agent");
+    return machine + " / " + servletPath + " / " + agent;
   }
 
   private String getModuleName(String requestURI) {