Adds non-trivial tests to the default app created by webAppCreator, including one that uses delayTestFinish and sends an RPC.  Also shows an example of shared client/server code.

Patch by: jlabanca
Review by: jgw



git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@7362 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/user/tools/AppClassTemplate.javasrc b/user/src/com/google/gwt/user/tools/AppClassTemplate.javasrc
index 433a4d0..45429c3 100644
--- a/user/src/com/google/gwt/user/tools/AppClassTemplate.javasrc
+++ b/user/src/com/google/gwt/user/tools/AppClassTemplate.javasrc
@@ -1,5 +1,6 @@
 package @clientPackage;
 
+import @sharedPackage.FieldVerifier;
 import com.google.gwt.core.client.EntryPoint;
 import com.google.gwt.core.client.GWT;
 import com.google.gwt.event.dom.client.ClickEvent;
@@ -40,6 +41,7 @@
     final Button sendButton = new Button("Send");
     final TextBox nameField = new TextBox();
     nameField.setText("GWT User");
+    final Label errorLabel = new Label();
 
     // We can add style names to widgets
     sendButton.addStyleName("sendButton");
@@ -48,6 +50,7 @@
     // Use RootPanel.get() to get the entire body element
     RootPanel.get("nameFieldContainer").add(nameField);
     RootPanel.get("sendButtonContainer").add(sendButton);
+    RootPanel.get("errorLabelContainer").add(errorLabel);
 
     // Focus the cursor on the name field when the app loads
     nameField.setFocus(true);
@@ -103,8 +106,16 @@
        * Send the name from the nameField to the server and wait for a response.
        */
       private void sendNameToServer() {
-        sendButton.setEnabled(false);
+        // First, we validate the input.
+        errorLabel.setText("");
         String textToServer = nameField.getText();
+        if (!FieldVerifier.isValidName(textToServer)) {
+          errorLabel.setText("Please enter at least four characters");
+          return;
+        }
+        
+        // Then, we send the input to the server.
+        sendButton.setEnabled(false);
         textToServerLabel.setText(textToServer);
         serverResponseLabel.setText("");
         greetingService.greetServer(textToServer, new AsyncCallback<String>() {
diff --git a/user/src/com/google/gwt/user/tools/AppHtml.htmlsrc b/user/src/com/google/gwt/user/tools/AppHtml.htmlsrc
index e85d026..75007e9 100644
--- a/user/src/com/google/gwt/user/tools/AppHtml.htmlsrc
+++ b/user/src/com/google/gwt/user/tools/AppHtml.htmlsrc
@@ -56,6 +56,9 @@
         <td id="nameFieldContainer"></td>
         <td id="sendButtonContainer"></td>
       </tr>
+      <tr>
+        <td colspan="2" style="color:red;" id="errorLabelContainer"></td>
+      </tr>
     </table>
   </body>
 </html>
diff --git a/user/src/com/google/gwt/user/tools/JUnit.gwt.xmlsrc b/user/src/com/google/gwt/user/tools/JUnit.gwt.xmlsrc
new file mode 100644
index 0000000..5d70944
--- /dev/null
+++ b/user/src/com/google/gwt/user/tools/JUnit.gwt.xmlsrc
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>@gwtModuleDtd
+<module>
+  <!-- Inherit our applications main module.                      -->
+  <inherits name='@moduleName'/>
+
+  <!-- Specify the path to any remote services.                   -->
+  <servlet path="/@renameTo/greet" class="@serverPackage.GreetingServiceImpl" />
+
+</module>
diff --git a/user/src/com/google/gwt/user/tools/JUnitClassTemplate.javasrc b/user/src/com/google/gwt/user/tools/JUnitClassTemplate.javasrc
index 25a6a58..b63904b 100644
--- a/user/src/com/google/gwt/user/tools/JUnitClassTemplate.javasrc
+++ b/user/src/com/google/gwt/user/tools/JUnitClassTemplate.javasrc
@@ -1,6 +1,10 @@
 package @clientPackage;
 
+import @sharedPackage.FieldVerifier;
+import com.google.gwt.core.client.GWT;
 import com.google.gwt.junit.client.GWTTestCase;
+import com.google.gwt.user.client.rpc.AsyncCallback;
+import com.google.gwt.user.client.rpc.ServiceDefTarget;
 
 /**
  * GWT JUnit tests must extend GWTTestCase.
@@ -11,14 +15,54 @@
    * Must refer to a valid module that sources this class.
    */
   public String getModuleName() {
-    return "@moduleName";
+    return "@moduleNameJUnit";
   }
 
   /**
-   * Add as many tests as you like.
+   * Tests the FieldVerifier.
    */
-  public void testSimple() {
-    assertTrue(true);
+  public void testFieldVerifier() {
+    assertFalse(FieldVerifier.isValidName(null));
+    assertFalse(FieldVerifier.isValidName(""));
+    assertFalse(FieldVerifier.isValidName("a"));
+    assertFalse(FieldVerifier.isValidName("ab"));
+    assertFalse(FieldVerifier.isValidName("abc"));
+    assertTrue(FieldVerifier.isValidName("abcd"));
   }
 
+  /**
+   * This test will send a request to the server using the greetServer method in
+   * GreetingService and verify the response.
+   */
+  public void testGreetingService() {
+    // Create the service that we will test.
+    GreetingServiceAsync greetingService = GWT.create(GreetingService.class);
+    ServiceDefTarget target = (ServiceDefTarget) greetingService;
+    target.setServiceEntryPoint(GWT.getModuleBaseURL() + "/@renameTo/greet");
+
+    // Since RPC calls are asynchronous, we will need to wait for a response
+    // after this test method returns. This line tells the test runner to wait
+    // up to 10 seconds before timing out.
+    delayTestFinish(10000);
+
+    // Send a request to the server.
+    greetingService.greetServer("GWT User", new AsyncCallback<String>() {
+      public void onFailure(Throwable caught) {
+        // The request resulted in an unexpected error.
+        fail("Request failure: " + caught.getMessage());
+      }
+
+      public void onSuccess(String result) {
+        // Verify that the response is correct.
+        assertTrue(result.startsWith("Hello, GWT User!"));
+
+        // Now that we have received a response, we need to tell the test runner
+        // that the test is complete. You must call finishTest() after an
+        // asynchronous test finishes successfully, or the test will time out.
+        finishTest();
+      }
+    });
+  }
+
+
 }
diff --git a/user/src/com/google/gwt/user/tools/Module.gwt.xmlsrc b/user/src/com/google/gwt/user/tools/Module.gwt.xmlsrc
index 8be3e85..2f0267e 100644
--- a/user/src/com/google/gwt/user/tools/Module.gwt.xmlsrc
+++ b/user/src/com/google/gwt/user/tools/Module.gwt.xmlsrc
@@ -17,5 +17,6 @@
 
   <!-- Specify the paths for translatable code                    -->
   <source path='client'/>
+  <source path='shared'/>
 
 </module>
diff --git a/user/src/com/google/gwt/user/tools/RpcAsyncClientTemplate.javasrc b/user/src/com/google/gwt/user/tools/RpcAsyncClientTemplate.javasrc
index e91fb2e..fe1abcc 100644
--- a/user/src/com/google/gwt/user/tools/RpcAsyncClientTemplate.javasrc
+++ b/user/src/com/google/gwt/user/tools/RpcAsyncClientTemplate.javasrc
@@ -6,5 +6,6 @@
  * The async counterpart of <code>GreetingService</code>.
  */
 public interface GreetingServiceAsync {
-  void greetServer(String input, AsyncCallback<String> callback);
+  void greetServer(String input, AsyncCallback<String> callback)
+      throws IllegalArgumentException;
 }
diff --git a/user/src/com/google/gwt/user/tools/RpcClientTemplate.javasrc b/user/src/com/google/gwt/user/tools/RpcClientTemplate.javasrc
index 40fcca7..3a68c93 100644
--- a/user/src/com/google/gwt/user/tools/RpcClientTemplate.javasrc
+++ b/user/src/com/google/gwt/user/tools/RpcClientTemplate.javasrc
@@ -8,5 +8,5 @@
  */
 @RemoteServiceRelativePath("greet")
 public interface GreetingService extends RemoteService {
-  String greetServer(String name);
+  String greetServer(String name) throws IllegalArgumentException;
 }
diff --git a/user/src/com/google/gwt/user/tools/RpcServerTemplate.javasrc b/user/src/com/google/gwt/user/tools/RpcServerTemplate.javasrc
index 4278c86..8d00ff5 100644
--- a/user/src/com/google/gwt/user/tools/RpcServerTemplate.javasrc
+++ b/user/src/com/google/gwt/user/tools/RpcServerTemplate.javasrc
@@ -1,6 +1,7 @@
 package @serverPackage;
 
 import @clientPackage.GreetingService;
+import @sharedPackage.FieldVerifier;
 import com.google.gwt.user.server.rpc.RemoteServiceServlet;
 
 /**
@@ -10,7 +11,15 @@
 public class GreetingServiceImpl extends RemoteServiceServlet implements
     GreetingService {
 
-  public String greetServer(String input) {
+  public String greetServer(String input) throws IllegalArgumentException {
+    // Verify that the input is valid. 
+    if (!FieldVerifier.isValidName(input)) {
+      // If the input is not valid, throw an IllegalArgumentException back to
+      // the client.
+      throw new IllegalArgumentException(
+          "Name must be at least 4 characters long");
+    }
+
     String serverInfo = getServletContext().getServerInfo();
     String userAgent = getThreadLocalRequest().getHeader("User-Agent");
     return "Hello, " + input + "!<br><br>I am running " + serverInfo
diff --git a/user/src/com/google/gwt/user/tools/SharedClassTemplate.javasrc b/user/src/com/google/gwt/user/tools/SharedClassTemplate.javasrc
new file mode 100644
index 0000000..9a3bb0e
--- /dev/null
+++ b/user/src/com/google/gwt/user/tools/SharedClassTemplate.javasrc
@@ -0,0 +1,42 @@
+package @sharedPackage;
+
+/**
+ * <p>
+ * FieldVerifier validates that the name the user enters is valid.
+ * </p>
+ * <p>
+ * This class is in the <code>shared</code> packing because we use it in both
+ * the client code and on the server. On the client, we verify that the name is
+ * valid before sending an RPC request so the user doesn't have to wait for a
+ * network round trip to get feedback. On the server, we verify that the name is
+ * correct to ensure that the input is correct regardless of where the RPC
+ * originates.
+ * </p>
+ * <p>
+ * When creating a class that is used on both the client and the server, be sure
+ * that all code is translatable and does not use native JavaScript. Code that
+ * is note translatable (such as code that interacts with a database or the file
+ * system) cannot be compiled into client side JavaScript. Code that uses native
+ * JavaScript (such as Widgets) cannot be run on the server.
+ * </p>
+ */
+public class FieldVerifier {
+
+  /**
+   * Verifies that the specified name is valid for our service.
+   * 
+   * In this example, we only require that the name is at least four
+   * characters. In your application, you can use more complex checks to ensure
+   * that usernames, passwords, email addresses, URLs, and other fields have the
+   * proper syntax.
+   * 
+   * @param name the name to validate
+   * @return true if valid, false if invalid
+   */
+  public static boolean isValidName(String name) {
+    if (name == null) {
+      return false;
+    }
+    return name.length() > 3;
+  }
+}
diff --git a/user/src/com/google/gwt/user/tools/WebAppCreator.java b/user/src/com/google/gwt/user/tools/WebAppCreator.java
index ab20988..667de6d 100644
--- a/user/src/com/google/gwt/user/tools/WebAppCreator.java
+++ b/user/src/com/google/gwt/user/tools/WebAppCreator.java
@@ -290,8 +290,10 @@
         '.', '/'), true);
     File clientDir = Utility.getDirectory(moduleDir, "client", true);
     File serverDir = Utility.getDirectory(moduleDir, "server", true);
-    File clientTestDir = Utility.getDirectory(outDir, "test/"
-        + modulePackageName.replace('.', '/') + "/client", true);
+    File sharedDir = Utility.getDirectory(moduleDir, "shared", true);
+    File moduleTestDir = Utility.getDirectory(outDir, "test/"
+        + modulePackageName.replace('.', '/'), true);
+    File clientTestDir = Utility.getDirectory(moduleTestDir, "client", true);
 
     // Create a map of replacements
     Map<String, String> replacements = new HashMap<String, String>();
@@ -299,6 +301,7 @@
     replacements.put("@moduleName", moduleName);
     replacements.put("@clientPackage", modulePackageName + ".client");
     replacements.put("@serverPackage", modulePackageName + ".server");
+    replacements.put("@sharedPackage", modulePackageName + ".shared");
     replacements.put("@gwtSdk", installPath);
     replacements.put("@gwtUserPath", gwtUserPath);
     replacements.put("@gwtDevPath", gwtDevPath);
@@ -361,16 +364,20 @@
       files.add(new FileCreator(webInfDir, "web.xml", "web.xml"));
       files.add(new FileCreator(clientDir, moduleShortName + ".java",
           "AppClassTemplate.java"));
-      files.add(new FileCreator(clientDir, "GreetingService" + ".java",
+      files.add(new FileCreator(clientDir, "GreetingService.java",
           "RpcClientTemplate.java"));
-      files.add(new FileCreator(clientDir, "GreetingServiceAsync" + ".java",
+      files.add(new FileCreator(clientDir, "GreetingServiceAsync.java",
           "RpcAsyncClientTemplate.java"));
-      files.add(new FileCreator(serverDir, "GreetingServiceImpl" + ".java",
+      files.add(new FileCreator(serverDir, "GreetingServiceImpl.java",
           "RpcServerTemplate.java"));
+      files.add(new FileCreator(sharedDir, "FieldVerifier.java",
+          "SharedClassTemplate.java"));
       files.add(new FileCreator(outDir, "build.xml", "project.ant.xml"));
       files.add(new FileCreator(outDir, "README.txt", "README.txt"));
       if (junitPath != null) {
         // create the test file.
+        files.add(new FileCreator(moduleTestDir, moduleShortName
+            + "JUnit.gwt.xml", "JUnit.gwt.xml"));
         files.add(new FileCreator(clientTestDir, moduleShortName + "Test"
             + ".java", "JUnitClassTemplate.java"));
       }