Revision: 6279
Author: [email protected]
Date: Thu Oct  1 10:34:33 2009
Log: Test batches will be sent to the client as they are executed by  
JUnitShell if NoBatchingStrategy (the default) is used. This allows users  
to use test runners that shard test methods across different clients.

Patch by: jlabanca
Review by: fabbott


http://code.google.com/p/google-web-toolkit/source/detail?r=6279

Added:
  /trunk/user/test/com/google/gwt/junit/CompileStrategyTest.java
Modified:
  /trunk/user/src/com/google/gwt/junit/BatchingStrategy.java
  /trunk/user/src/com/google/gwt/junit/CompileStrategy.java
  /trunk/user/src/com/google/gwt/junit/JUnitShell.java
  /trunk/user/test/com/google/gwt/junit/BatchingStrategyTest.java
  /trunk/user/test/com/google/gwt/junit/JUnitSuite.java

=======================================
--- /dev/null
+++ /trunk/user/test/com/google/gwt/junit/CompileStrategyTest.java      Thu Oct 
  
1 10:34:33 2009
@@ -0,0 +1,274 @@
+/*
+ * Copyright 2009 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;
+
+import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.core.ext.UnableToCompleteException;
+import com.google.gwt.dev.cfg.ModuleDef;
+import com.google.gwt.junit.JUnitShell.Strategy;
+import com.google.gwt.junit.client.GWTTestCase;
+import com.google.gwt.junit.client.impl.JUnitHost.TestInfo;
+
+import junit.framework.TestCase;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Tests of {...@link BatchingStrategy}. This test must run after a
+ * {...@link GWTTestCase} to ensure that JUnitShell is already initialized.
+ */
+public class CompileStrategyTest extends TestCase {
+
+  /**
+   * A mock {...@link RunStyle} used for testing.
+   */
+  private static class MockRunStyle extends RunStyle {
+
+    public MockRunStyle() {
+      super(null);
+    }
+
+    @Override
+    public boolean isLocal() {
+      return true;
+    }
+
+    @Override
+    public void launchModule(String moduleName) {
+    }
+
+    @Override
+    public void maybeCompileModule(String moduleName) {
+    }
+  }
+
+  /**
+   * A mock {...@link JUnitMessageQueue} used for testing.
+   */
+  private static class MockJUnitMessageQueue extends JUnitMessageQueue {
+
+    /**
+     * The test blocks added to the queue.
+     */
+    private List<TestInfo[]> testBlocks;
+
+    /**
+     * Indicates that this is the last test block.
+     */
+    private boolean isLastBlock;
+
+    public MockJUnitMessageQueue() {
+      super(1);
+    }
+
+    @Override
+    void addTestBlocks(List<TestInfo[]> newTestBlocks, boolean  
isLastBlock) {
+      assertNull(testBlocks);
+      this.testBlocks = newTestBlocks;
+      this.isLastBlock = isLastBlock;
+    }
+
+    void assertIsLastBlock(boolean expected) {
+      assertEquals(expected, isLastBlock);
+    }
+
+    void assertTestBlocks(List<TestInfo[]> expected) {
+      if (expected == null || testBlocks == null) {
+        assertEquals(expected, testBlocks);
+        return;
+      }
+
+      assertEquals(expected.size(), testBlocks.size());
+      for (int i = 0; i < testBlocks.size(); i++) {
+        TestInfo[] actualBlock = testBlocks.get(i);
+        TestInfo[] expectedBlock = expected.get(i);
+        assertEquals(expectedBlock.length, actualBlock.length);
+        for (int j = 0; j < expectedBlock.length; j++) {
+          assertEquals(expectedBlock[j], actualBlock[j]);
+        }
+      }
+    }
+  }
+
+  /**
+   * A mock {...@link CompileStrategy} used for testing.
+   */
+  private static class MockCompileStrategy extends CompileStrategy {
+
+    /**
+     * The number of modules to mock.
+     */
+    private int mockModuleCount;
+
+    private MockJUnitMessageQueue messageQueue = new  
MockJUnitMessageQueue();
+
+    /**
+     * Construct a new {...@link MockCompileStrategy}.
+     *
+     * @param mockModuleCount the number of modules
+     */
+    public MockCompileStrategy(int mockModuleCount) {
+      this.mockModuleCount = mockModuleCount;
+    }
+
+    @Override
+    public ModuleDef maybeCompileModule(String moduleName,
+        String syntheticModuleName, Strategy strategy, RunStyle runStyle,
+        BatchingStrategy batchingStrategy, TreeLogger treeLogger) {
+      fail("This method should not be called.");
+      return null;
+    }
+
+    @Override
+    MockJUnitMessageQueue getMessageQueue() {
+      return messageQueue;
+    }
+
+    @Override
+    int getModuleCount() {
+      return mockModuleCount;
+    }
+
+    @Override
+    ModuleDef maybeCompileModuleImpl2(String moduleName,
+        String syntheticModuleName, Strategy strategy, RunStyle runStyle,
+        TreeLogger treeLogger) {
+      return null;
+    }
+  }
+
+  /**
+   * A mock test case used for testing.
+   */
+  private static class MockGWTTestCase extends GWTTestCase {
+    @Override
+    public String getModuleName() {
+      return "com.google.gwt.junit.JUnit";
+    }
+
+    @Override
+    public String getName() {
+      return "testMethod1";
+    }
+
+    public void testMethod0() {
+    }
+
+    public void testMethod1() {
+    }
+
+    public void testMethod2() {
+    }
+  }
+
+  public void testMaybeAddTestBlockForCurrentTestWithoutBatching() {
+    BatchingStrategy batchingStrategy = new NoBatchingStrategy();
+    assertTrue(batchingStrategy.isSingleTestOnly());
+
+    // Maybe add the current test.
+    GWTTestCase testCase = new MockGWTTestCase();
+    MockCompileStrategy strategy = new MockCompileStrategy(-1);
+    strategy.maybeAddTestBlockForCurrentTest(testCase, batchingStrategy);
+
+    // Generate the expected blocks.
+    TestInfo testInfo = new TestInfo(testCase.getSyntheticModuleName(),
+        testCase.getClass().getName(), testCase.getName());
+    List<TestInfo[]> testBlocks = new ArrayList<TestInfo[]>();
+    testBlocks.add(new TestInfo[] {testInfo});
+
+    // Verify the test is added to the queue.
+    strategy.getMessageQueue().assertIsLastBlock(false);
+    strategy.getMessageQueue().assertTestBlocks(testBlocks);
+  }
+
+  public void testMaybeAddTestBlockForCurrentTestWithBatching() {
+    BatchingStrategy batchingStrategy = new ModuleBatchingStrategy();
+    assertFalse(batchingStrategy.isSingleTestOnly());
+
+    // Maybe add the current test.
+    GWTTestCase testCase = new MockGWTTestCase();
+    MockCompileStrategy strategy = new MockCompileStrategy(-1);
+    strategy.maybeAddTestBlockForCurrentTest(testCase, batchingStrategy);
+
+    // Verify the test is not added to the queue.
+    strategy.getMessageQueue().assertTestBlocks(null);
+  }
+
+  public void testMaybeCompileModuleImplWithoutBatching() {
+    BatchingStrategy batchingStrategy = new NoBatchingStrategy();
+    assertTrue(batchingStrategy.isSingleTestOnly());
+
+    // Maybe add the current test.
+    RunStyle runStyle = new MockRunStyle();
+    GWTTestCase testCase = new MockGWTTestCase();
+    MockCompileStrategy strategy = new MockCompileStrategy(-1);
+    try {
+      strategy.maybeCompileModuleImpl(testCase.getModuleName(),
+          testCase.getSyntheticModuleName(), testCase.getStrategy(),  
runStyle,
+          batchingStrategy, TreeLogger.NULL);
+    } catch (UnableToCompleteException e) {
+      fail("Unexpected UnableToCompleteException: " + e.getMessage());
+    }
+
+    // Verify the test block is not added to the queue.
+    strategy.getMessageQueue().assertTestBlocks(null);
+  }
+
+  public void testMaybeCompileModuleImplWithBatchingLastModule() {
+    BatchingStrategy batchingStrategy = new ModuleBatchingStrategy();
+    assertFalse(batchingStrategy.isSingleTestOnly());
+
+    // Maybe add the current test.
+    RunStyle runStyle = new MockRunStyle();
+    GWTTestCase testCase = new MockGWTTestCase();
+    MockCompileStrategy strategy = new MockCompileStrategy(-1);
+    try {
+      strategy.maybeCompileModuleImpl(testCase.getModuleName(),
+          testCase.getSyntheticModuleName(), testCase.getStrategy(),  
runStyle,
+          batchingStrategy, TreeLogger.NULL);
+    } catch (UnableToCompleteException e) {
+      fail("Unexpected UnableToCompleteException: " + e.getMessage());
+    }
+
+    // Verify the test block is added to the queue.
+    strategy.getMessageQueue().assertIsLastBlock(true);
+    strategy.getMessageQueue().assertTestBlocks(
+        batchingStrategy.getTestBlocks(testCase.getSyntheticModuleName()));
+  }
+
+  public void testMaybeCompileModuleImplWithBatchingNotLastModule() {
+    BatchingStrategy batchingStrategy = new ClassBatchingStrategy();
+    assertFalse(batchingStrategy.isSingleTestOnly());
+
+    // Maybe add the current test.
+    RunStyle runStyle = new MockRunStyle();
+    GWTTestCase testCase = new MockGWTTestCase();
+    MockCompileStrategy strategy = new MockCompileStrategy(1000);
+    try {
+      strategy.maybeCompileModuleImpl(testCase.getModuleName(),
+          testCase.getSyntheticModuleName(), testCase.getStrategy(),  
runStyle,
+          batchingStrategy, TreeLogger.NULL);
+    } catch (UnableToCompleteException e) {
+      fail("Unexpected UnableToCompleteException: " + e.getMessage());
+    }
+
+    // Verify the test block is added to the queue.
+    strategy.getMessageQueue().assertIsLastBlock(false);
+    strategy.getMessageQueue().assertTestBlocks(
+        batchingStrategy.getTestBlocks(testCase.getSyntheticModuleName()));
+  }
+}
=======================================
--- /trunk/user/src/com/google/gwt/junit/BatchingStrategy.java  Wed Sep 30  
12:07:05 2009
+++ /trunk/user/src/com/google/gwt/junit/BatchingStrategy.java  Thu Oct  1  
10:34:33 2009
@@ -39,6 +39,21 @@
     */
    public abstract List<TestInfo[]> getTestBlocks(String  
syntheticModuleName);

+  /**
+   * Check if this batching strategy only supports execution of a single  
test at
+   * a time.
+   *
+   * If this method returns true, test methods will be executed on the  
client as
+   * they are run by JUnit. If it returns false, test methods will be  
batched
+   * and sent to the clients in groups. If you are using a test runner that
+   * shards test methods across multiple clients, you should use a  
strategy that
+   * returns false (such as {...@link NoBatchingStrategy}) or all tests will  
be
+   * executed on all clients.
+   *
+   * @return true if batches never contain more than one test
+   */
+  public abstract boolean isSingleTestOnly();
+
    /**
     * Get the set of tests for this module, minus tests that should not be
     * executed.
@@ -72,6 +87,11 @@
      }
      return testBlocks;
    }
+
+  @Override
+  public boolean isSingleTestOnly() {
+    return true;
+  }
  }

  /**
@@ -105,6 +125,11 @@
      }
      return testBlocks;
    }
+
+  @Override
+  public boolean isSingleTestOnly() {
+    return false;
+  }
  }

  /**
@@ -121,4 +146,9 @@
      }
      return testBlocks;
    }
-}
+
+  @Override
+  public boolean isSingleTestOnly() {
+    return false;
+  }
+}
=======================================
--- /trunk/user/src/com/google/gwt/junit/CompileStrategy.java   Thu Sep 17  
13:59:15 2009
+++ /trunk/user/src/com/google/gwt/junit/CompileStrategy.java   Thu Oct  1  
10:34:33 2009
@@ -43,6 +43,23 @@
     * adding test batches that have already been added.
     */
    private Set<String> compiledModuleNames = new HashSet<String>();
+
+  /**
+   * Maybe add a test block for the currently executed test case.
+   *
+   * @param testCase the test case being run
+   * @param batchingStrategy the batching strategy
+   */
+  public void maybeAddTestBlockForCurrentTest(GWTTestCase testCase,
+      BatchingStrategy batchingStrategy) {
+    if (batchingStrategy.isSingleTestOnly()) {
+      TestInfo testInfo = new TestInfo(testCase.getSyntheticModuleName(),
+          testCase.getClass().getName(), testCase.getName());
+      List<TestInfo[]> testBlocks = new ArrayList<TestInfo[]>(1);
+      testBlocks.add(new TestInfo[] {testInfo});
+      getMessageQueue().addTestBlocks(testBlocks, false);
+    }
+  }

    /**
     * Let the compile strategy compile another module. This is called while
@@ -85,6 +102,57 @@
        BatchingStrategy batchingStrategy, TreeLogger treeLogger)
        throws UnableToCompleteException {

+    // Let the runstyle compile the module.
+    ModuleDef moduleDef = maybeCompileModuleImpl2(moduleName,
+        syntheticModuleName, strategy, runStyle, treeLogger);
+
+    // Add all test blocks for the module if we haven't seen this module  
before.
+    if (!compiledModuleNames.contains(syntheticModuleName)) {
+      compiledModuleNames.add(syntheticModuleName);
+      if (!batchingStrategy.isSingleTestOnly()) {
+        // Use >= so we can mock getModuleCount and force isFinalModule to  
true
+        boolean isFinalModule = compiledModuleNames.size() >=  
getModuleCount();
+        List<TestInfo[]> testBlocks =  
batchingStrategy.getTestBlocks(syntheticModuleName);
+        getMessageQueue().addTestBlocks(testBlocks, isFinalModule);
+      }
+    }
+
+    return moduleDef;
+  }
+
+  /**
+   * Visible for testing and mocking.
+   *
+   * @return the {...@link JUnitMessageQueue}
+   */
+  JUnitMessageQueue getMessageQueue() {
+    return JUnitShell.getMessageQueue();
+  }
+
+  /**
+   * Visible for testing and mocking.
+   *
+   * @return the number of modules to test
+   */
+  int getModuleCount() {
+    return GWTTestCase.getModuleCount();
+  }
+
+  /**
+   * Let the {...@link RunStyle} compile the module if needed
+   *
+   * Visible for testing and mocking.
+   *
+   * @param moduleName the module name
+   * @param syntheticModuleName the synthetic module name
+   * @param strategy the strategy
+   * @param runStyle the run style
+   * @param treeLogger the logger
+   * @return the {...@link ModuleDef} describing the synthetic module
+   */
+  ModuleDef maybeCompileModuleImpl2(String moduleName,
+      String syntheticModuleName, Strategy strategy, RunStyle runStyle,
+      TreeLogger treeLogger) throws UnableToCompleteException {
      /*
       * Synthesize a synthetic module that derives from the user-specified  
module
       * but also includes JUnit support.
@@ -103,14 +171,6 @@
      moduleNameProp.setValue(syntheticModuleName);

      runStyle.maybeCompileModule(syntheticModuleName);
-
-    // Add all test blocks for the module if we haven't seen this module  
before.
-    if (!compiledModuleNames.contains(syntheticModuleName)) {
-      compiledModuleNames.add(syntheticModuleName);
-      boolean isFinalModule = compiledModuleNames.size() ==  
GWTTestCase.getModuleCount();
-      List<TestInfo[]> testBlocks =  
batchingStrategy.getTestBlocks(syntheticModuleName);
-      JUnitShell.getMessageQueue().addTestBlocks(testBlocks,  
isFinalModule);
-    }

      return moduleDef;
    }
=======================================
--- /trunk/user/src/com/google/gwt/junit/JUnitShell.java        Wed Sep 30  
12:07:05 2009
+++ /trunk/user/src/com/google/gwt/junit/JUnitShell.java        Thu Oct  1  
10:34:33 2009
@@ -1005,6 +1005,7 @@
        processTestResult(testCase, testResult, strategy);
        return;
      }
+    compileStrategy.maybeAddTestBlockForCurrentTest(testCase,  
batchingStrategy);

      try {
        if (firstLaunch) {
=======================================
--- /trunk/user/test/com/google/gwt/junit/BatchingStrategyTest.java     Wed Sep 
 
30 12:07:05 2009
+++ /trunk/user/test/com/google/gwt/junit/BatchingStrategyTest.java     Thu  
Oct  1 10:34:33 2009
@@ -79,22 +79,28 @@
        FAKE_MODULE_SYNTHETIC_NAME,  
TestClass2.class.getName(), "testMethod5");

    public void testClassBatchingStrategy() {
+    BatchingStrategy strategy = new ClassBatchingStrategy();
+    assertFalse(strategy.isSingleTestOnly());
      List<TestInfo[]> testBlocks = new ArrayList<TestInfo[]>();
      testBlocks.add(new TestInfo[] {TEST_INFO_0_0, TEST_INFO_0_1});
      testBlocks.add(new TestInfo[] {TEST_INFO_1_2, TEST_INFO_1_3,  
TEST_INFO_1_4});
      testBlocks.add(new TestInfo[] {TEST_INFO_2_5});
-    testBatchingStrategy(new ClassBatchingStrategy(), testBlocks);
+    testBatchingStrategy(strategy, testBlocks);
    }

    public void testModuleBatchingStrategy() {
+    BatchingStrategy strategy = new ModuleBatchingStrategy();
+    assertFalse(strategy.isSingleTestOnly());
      List<TestInfo[]> testBlocks = new ArrayList<TestInfo[]>();
      testBlocks.add(new TestInfo[] {
          TEST_INFO_0_0, TEST_INFO_0_1, TEST_INFO_1_2, TEST_INFO_1_3,
          TEST_INFO_1_4, TEST_INFO_2_5});
-    testBatchingStrategy(new ModuleBatchingStrategy(), testBlocks);
+    testBatchingStrategy(strategy, testBlocks);
    }

    public void testNoBatchingStrategy() {
+    BatchingStrategy strategy = new NoBatchingStrategy();
+    assertTrue(strategy.isSingleTestOnly());
      List<TestInfo[]> testBlocks = new ArrayList<TestInfo[]>();
      testBlocks.add(new TestInfo[] {TEST_INFO_0_0});
      testBlocks.add(new TestInfo[] {TEST_INFO_0_1});
@@ -102,7 +108,7 @@
      testBlocks.add(new TestInfo[] {TEST_INFO_1_3});
      testBlocks.add(new TestInfo[] {TEST_INFO_1_4});
      testBlocks.add(new TestInfo[] {TEST_INFO_2_5});
-    testBatchingStrategy(new NoBatchingStrategy(), testBlocks);
+    testBatchingStrategy(strategy, testBlocks);
    }

    /**
=======================================
--- /trunk/user/test/com/google/gwt/junit/JUnitSuite.java       Wed Sep 30  
12:07:05 2009
+++ /trunk/user/test/com/google/gwt/junit/JUnitSuite.java       Thu Oct  1  
10:34:33 2009
@@ -25,7 +25,8 @@
   */
  public class JUnitSuite {
    public static Test suite() {
-    GWTTestSuite suite = new GWTTestSuite("Test for suite for  
com.google.gwt.junit");
+    GWTTestSuite suite = new GWTTestSuite(
+        "Test for suite for com.google.gwt.junit");

      // client
      // Suppressed due to flakiness on Linux
@@ -34,6 +35,7 @@

      // Must run after a GWTTestCase so JUnitShell is initialized.
      suite.addTestSuite(BatchingStrategyTest.class);
+    suite.addTestSuite(CompileStrategyTest.class);

      suite.addTestSuite(FakeMessagesMakerTest.class);
      suite.addTestSuite(JUnitMessageQueueTest.class);

--~--~---------~--~----~------------~-------~--~----~
http://groups.google.com/group/Google-Web-Toolkit-Contributors
-~----------~----~----~----~------~----~------~--~---

Reply via email to