Adding a feature that allows users to define compile properties on test methods in GwtTestCases.  A synthetic module will be created using the test module plus the specified properties.

Patch by: pmuetschard   	
Review by: jlabanca



git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@6425 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/benchmarks/client/Benchmark.java b/user/src/com/google/gwt/benchmarks/client/Benchmark.java
index 1532279..b0b2db6 100644
--- a/user/src/com/google/gwt/benchmarks/client/Benchmark.java
+++ b/user/src/com/google/gwt/benchmarks/client/Benchmark.java
@@ -17,6 +17,7 @@
 
 import com.google.gwt.benchmarks.BenchmarkShell;
 import com.google.gwt.benchmarks.client.impl.BenchmarkResults;
+import com.google.gwt.junit.PropertyDefiningStrategy;
 import com.google.gwt.junit.JUnitShell.Strategy;
 import com.google.gwt.junit.client.GWTTestCase;
 import com.google.gwt.junit.client.impl.JUnitResult;
@@ -107,31 +108,34 @@
   /**
    * The {@link Strategy} used for benchmarking.
    */
-  private static Strategy BENCHMARK_STRATEGY = new Strategy() {
+  public static class BenchmarkStrategy extends PropertyDefiningStrategy {
+    public BenchmarkStrategy(TestCase test) {
+      super(test);
+    }
+
+    @Override
     public String getModuleInherit() {
       return "com.google.gwt.benchmarks.Benchmarks";
     }
 
-    public String getSyntheticModuleExtension() {
-      return "Benchmarks";
-    }
-
+    @Override
     public void processResult(TestCase testCase, JUnitResult result) {
+      super.processResult(testCase, result);
       if (result instanceof BenchmarkResults) {
         BenchmarkShell.getReport().addBenchmarkResults(testCase,
             (BenchmarkResults) result);
       }
     }
-  };
 
-  /**
-   * Get the {@link Strategy} to use when compiling and running this test.
-   *  
-   * @return the test {@link Strategy}
-   */
+    @Override
+    protected String getBaseModuleExtension() {
+      return "Benchmarks";
+    }
+  }
+
   @Override
-  public Strategy getStrategy() {
-    return BENCHMARK_STRATEGY;
+  protected Strategy createStrategy() {
+    return new BenchmarkStrategy(this);
   }
 
   /**
diff --git a/user/src/com/google/gwt/junit/CompileStrategy.java b/user/src/com/google/gwt/junit/CompileStrategy.java
index c69f645..abc2df8 100644
--- a/user/src/com/google/gwt/junit/CompileStrategy.java
+++ b/user/src/com/google/gwt/junit/CompileStrategy.java
@@ -182,6 +182,8 @@
         "junit.moduleName", false);
     moduleNameProp.setValue(syntheticModuleName);
 
+    strategy.processModule(moduleDef);
+
     junitShell.maybeCompileForWebMode(syntheticModuleName);
 
     return moduleDef;
diff --git a/user/src/com/google/gwt/junit/JUnitShell.java b/user/src/com/google/gwt/junit/JUnitShell.java
index 55152b6..99a42e3 100644
--- a/user/src/com/google/gwt/junit/JUnitShell.java
+++ b/user/src/com/google/gwt/junit/JUnitShell.java
@@ -94,6 +94,8 @@
 
     String getSyntheticModuleExtension();
 
+    void processModule(ModuleDef module);
+
     void processResult(TestCase testCase, JUnitResult result);
   }
 
diff --git a/user/src/com/google/gwt/junit/PropertyDefiningStrategy.java b/user/src/com/google/gwt/junit/PropertyDefiningStrategy.java
new file mode 100644
index 0000000..b10af04
--- /dev/null
+++ b/user/src/com/google/gwt/junit/PropertyDefiningStrategy.java
@@ -0,0 +1,133 @@
+/*
+ * 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.dev.cfg.BindingProperty;
+import com.google.gwt.dev.cfg.ModuleDef;
+import com.google.gwt.dev.cfg.Properties;
+import com.google.gwt.dev.util.Util;
+import com.google.gwt.junit.client.GWTTestCase;
+import com.google.gwt.junit.client.WithProperties;
+import com.google.gwt.junit.client.WithProperties.Property;
+
+import junit.framework.TestCase;
+
+import java.lang.reflect.Method;
+import java.util.Comparator;
+import java.util.Set;
+import java.util.TreeSet;
+
+/**
+ * A {@link JUnitShell.Strategy} that will alter the module the tests are run
+ * in by defining module properties as requested by annotations on the tests.
+ */
+public class PropertyDefiningStrategy extends GWTTestCase.BaseStrategy {
+  private TestCase testCase;
+  private Set<Property> properties;
+
+  public PropertyDefiningStrategy(TestCase testCase) {
+    this.testCase = testCase;
+  }
+
+  protected String getBaseModuleExtension() {
+    return super.getSyntheticModuleExtension();
+  }
+
+  @Override
+  public String getSyntheticModuleExtension() {
+    String extension = getBaseModuleExtension();
+    computePropertiesMap();
+    if (properties.size() > 0) {
+      StringBuilder sb = new StringBuilder();
+      for (Property property : properties) {
+        sb.append(".").append(property.name()).append(
+            "$").append(property.value());
+      }
+      extension += sb.toString();
+    }
+    return extension;
+  }
+
+  @Override
+  public void processModule(ModuleDef module) {
+    super.processModule(module);
+    computePropertiesMap();
+    if (properties.size() > 0) {
+      Properties props = module.getProperties();
+      for (Property property : properties) {
+        BindingProperty binding = props.createBinding(property.name());
+        if (!binding.isDefinedValue(property.value())) {
+          binding.addDefinedValue(
+              binding.getRootCondition(), property.value());
+        }
+        binding.setAllowedValues(
+            binding.getRootCondition(), property.value());
+      }
+    }
+  }
+
+  private Property checkProperty(Property property) {
+    String[] tokens = (property.name() + ". ").split("\\.");
+    for (int i = 0; i < tokens.length - 1; i++) {
+      if (!Util.isValidJavaIdent(tokens[i])) {
+        throw new AssertionError(
+            "Property name invalid: " + property.name());
+      }
+    }
+
+    if (!Util.isValidJavaIdent(property.value())) {
+      throw new AssertionError(
+          "Property value invalid: " + property.value());
+    }
+
+    return property;
+  }
+
+  private void computePropertiesMap() {
+    if (properties == null) {
+      Set<Property> props = new TreeSet<Property>(
+          new Comparator<Property>() {
+        public int compare(Property p1, Property p2) {
+          int r = p1.name().compareTo(p2.name());
+          if (r == 0) {
+            r = p1.value().compareTo(p2.value());
+          }
+          return r;
+        }
+      });
+      try {
+        String name = testCase.getName();
+        if (name != null) {
+          Method testMethod = testCase.getClass().getMethod(testCase.getName());
+          if (testMethod.isAnnotationPresent(WithProperties.class)) {
+            WithProperties annotation = 
+              testMethod.getAnnotation(WithProperties.class);
+            for (Property property : annotation.value()) {
+              props.add(checkProperty(property));
+            }
+          }
+        }
+      } catch (SecurityException e) {
+        // should not happen
+        e.printStackTrace();
+      } catch (NoSuchMethodException e) {
+        // should not happen
+        e.printStackTrace();
+      }
+      properties = props;
+    }
+  }
+}
diff --git a/user/src/com/google/gwt/junit/client/GWTTestCase.java b/user/src/com/google/gwt/junit/client/GWTTestCase.java
index 176c8a0..29c6fad 100644
--- a/user/src/com/google/gwt/junit/client/GWTTestCase.java
+++ b/user/src/com/google/gwt/junit/client/GWTTestCase.java
@@ -15,7 +15,9 @@
  */
 package com.google.gwt.junit.client;
 
+import com.google.gwt.dev.cfg.ModuleDef;
 import com.google.gwt.junit.JUnitShell;
+import com.google.gwt.junit.PropertyDefiningStrategy;
 import com.google.gwt.junit.JUnitShell.Strategy;
 import com.google.gwt.junit.client.impl.JUnitResult;
 import com.google.gwt.junit.client.impl.JUnitHost.TestInfo;
@@ -45,6 +47,25 @@
 public abstract class GWTTestCase extends TestCase {
 
   /**
+   * The base class for strategies to use for tests.
+   */
+  public static class BaseStrategy implements Strategy {
+    public String getModuleInherit() {
+      return "com.google.gwt.junit.JUnit";
+    }
+
+    public String getSyntheticModuleExtension() {
+      return "JUnit";
+    }
+
+    public void processModule(ModuleDef module) {
+    }
+
+    public void processResult(TestCase testCase, JUnitResult result) {
+    }
+  }
+
+  /**
    * Information about a synthetic module used for testing.
    */
   public static final class TestModuleInfo {
@@ -104,22 +125,6 @@
   private static final Object ALL_GWT_TESTS_LOCK = new Object();
 
   /**
-   * The default strategy to use for tests.
-   */
-  private static final Strategy DEFAULT_STRATEGY = new Strategy() {
-    public String getModuleInherit() {
-      return "com.google.gwt.junit.JUnit";
-    }
-
-    public String getSyntheticModuleExtension() {
-      return "JUnit";
-    }
-
-    public void processResult(TestCase testCase, JUnitResult result) {
-    }
-  };
-
-  /**
    * Get the names of all test modules.
    * 
    * @return all test module names
@@ -159,6 +164,11 @@
   protected TestResult testResult = null;
 
   /**
+   * The {@link Strategy} used by this test.
+   */
+  private Strategy strategy;
+
+  /**
    * A new instance of your subclass is constructed for each test method that is
    * to be run. You should avoid running code in your subclass constructor,
    * initializer blocks, and field initializations, because if those code blocks
@@ -246,7 +256,10 @@
    * @return the test {@link Strategy}
    */
   public Strategy getStrategy() {
-    return DEFAULT_STRATEGY;
+    if (strategy == null) {
+      strategy = createStrategy();
+    }
+    return strategy;
   }
 
   /**
@@ -291,6 +304,13 @@
   }
 
   /**
+   * Creates the test strategy to use (see {@link #getStrategy()}).
+   */
+  protected Strategy createStrategy() {
+    return new PropertyDefiningStrategy(this);
+  }
+
+  /**
    * Put the current test in asynchronous mode. If the test method completes
    * normally, this test will not immediately succeed. Instead, a <i>delay
    * period</i> begins. During the delay period, the test system will wait for
diff --git a/user/src/com/google/gwt/junit/client/WithProperties.java b/user/src/com/google/gwt/junit/client/WithProperties.java
new file mode 100644
index 0000000..5f1be89
--- /dev/null
+++ b/user/src/com/google/gwt/junit/client/WithProperties.java
@@ -0,0 +1,41 @@
+/*
+ * 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.client;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotation indicating that a test method inside a {@link GWTTestCase}
+ * requires a set of binding properties to be set in its module.
+ */
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface WithProperties {
+  Property[] value();
+
+  /**
+   * Annotation defining a binding property.
+   */
+  @Retention(RetentionPolicy.RUNTIME)
+  public static @interface Property {
+    String name();
+
+    String value();
+  }
+}
diff --git a/user/src/com/google/gwt/junit/tools/GWTTestSuite.java b/user/src/com/google/gwt/junit/tools/GWTTestSuite.java
index b76f540..d4c0c3d 100644
--- a/user/src/com/google/gwt/junit/tools/GWTTestSuite.java
+++ b/user/src/com/google/gwt/junit/tools/GWTTestSuite.java
@@ -80,7 +80,7 @@
 
     if (test instanceof GWTTestCase) {
       GWTTestCase gwtTest = (GWTTestCase) test;
-      String moduleName = gwtTest.getModuleName();
+      String moduleName = gwtTest.getSyntheticModuleName();
       if (moduleName != null) {
         TestSuite suite = moduleSuites.get(moduleName);
         if (suite == null) {
diff --git a/user/test/com/google/gwt/junit/JUnitSuite.java b/user/test/com/google/gwt/junit/JUnitSuite.java
index b7f67ce..79833e1 100644
--- a/user/test/com/google/gwt/junit/JUnitSuite.java
+++ b/user/test/com/google/gwt/junit/JUnitSuite.java
@@ -16,6 +16,7 @@
 package com.google.gwt.junit;
 
 import com.google.gwt.junit.client.GWTTestCaseTest;
+import com.google.gwt.junit.client.PropertyDefiningGWTTest;
 import com.google.gwt.junit.tools.GWTTestSuite;
 
 import junit.framework.Test;
@@ -50,6 +51,9 @@
     // Run manually only, launches servers that die on port contention
     // suite.addTestSuite(BrowserManagerServerTest.class);
 
+    suite.addTestSuite(PropertyDefiningStrategyTest.class);
+    suite.addTestSuite(PropertyDefiningGWTTest.class);
+
     return suite;
   }
 }
diff --git a/user/test/com/google/gwt/junit/JUnitTestWithProperties.gwt.xml b/user/test/com/google/gwt/junit/JUnitTestWithProperties.gwt.xml
new file mode 100644
index 0000000..ad0b17f
--- /dev/null
+++ b/user/test/com/google/gwt/junit/JUnitTestWithProperties.gwt.xml
@@ -0,0 +1,37 @@
+<!--                                                                        -->
+<!-- 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   -->
+<!-- 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. License for the specific language governing permissions and   -->
+<!-- limitations under the License.                                         -->
+
+<!-- This module is for the PropertyDefiningGWTTest -->
+<module>
+  <inherits name="com.google.gwt.user.User" />
+  <define-property name="my.property" values="one,two" />
+
+  <replace-with class="com.google.gwt.junit.client.PropertyDefiningGWTTest$TestImplOne">
+    <when-type-is class="com.google.gwt.junit.client.PropertyDefiningGWTTest$TestInterface" />
+    <when-property-is name="my.property" value="one" />
+  </replace-with>
+  <replace-with class="com.google.gwt.junit.client.PropertyDefiningGWTTest.TestImplOne">
+    <when-type-is class="com.google.gwt.junit.client.PropertyDefiningGWTTest.TestInterface" />
+    <when-property-is name="my.property" value="one" />
+  </replace-with>
+
+  <replace-with class="com.google.gwt.junit.client.PropertyDefiningGWTTest$TestImplTwo">
+    <when-type-is class="com.google.gwt.junit.client.PropertyDefiningGWTTest$TestInterface" />
+    <when-property-is name="my.property" value="two" />
+  </replace-with>
+  <replace-with class="com.google.gwt.junit.client.PropertyDefiningGWTTest.TestImplTwo">
+    <when-type-is class="com.google.gwt.junit.client.PropertyDefiningGWTTest.TestInterface" />
+    <when-property-is name="my.property" value="two" />
+  </replace-with>
+</module>
diff --git a/user/test/com/google/gwt/junit/PropertyDefiningStrategyTest.java b/user/test/com/google/gwt/junit/PropertyDefiningStrategyTest.java
new file mode 100644
index 0000000..e85ed86
--- /dev/null
+++ b/user/test/com/google/gwt/junit/PropertyDefiningStrategyTest.java
@@ -0,0 +1,113 @@
+/*
+ * 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.dev.cfg.BindingProperty;
+import com.google.gwt.dev.cfg.ModuleDef;
+import com.google.gwt.junit.client.WithProperties;
+import com.google.gwt.junit.client.WithProperties.Property;
+
+import junit.framework.TestCase;
+
+import java.util.SortedSet;
+
+/**
+ * Tests the {@link PropertyDefiningStrategy}.
+ */
+public class PropertyDefiningStrategyTest extends TestCase {
+  private static class PropertyValue {
+    public String name, value;
+
+    public PropertyValue(String name, String value) {
+      this.name = name;
+      this.value = value;
+    }
+  }
+
+  @WithProperties({@Property(name = "name", value = "value")})
+  public void methodWithSingleProperty() {
+  }
+
+  @WithProperties({
+      @Property(name = "name2", value = "value2"),
+      @Property(name = "name1", value = "value1")})
+  public void methodWithTwoProperties() {
+  }
+
+  public void methodWithoutProperties() {
+  }
+
+  public void testGetSyntheticModuleExtension() {
+    assertEquals("JUnit.name$value",
+        getStrategyForSingleProperty().getSyntheticModuleExtension());
+    assertEquals("JUnit.name1$value1.name2$value2",
+        getStrategyForTwoProperties().getSyntheticModuleExtension());
+    assertEquals("JUnit",
+        getStrategyForNoProperty().getSyntheticModuleExtension());
+  }
+
+  public void testProcessModuleForTestCaseWithSingleProperty() {
+    ModuleDef module = new ModuleDef("myModule");
+    getStrategyForSingleProperty().processModule(module);
+    assertProperties(module, p("name", "value"));
+  }
+
+  public void testProcessModuleForTestCaseWithTwoProperties() {
+    ModuleDef module = new ModuleDef("myModule");
+    getStrategyForTwoProperties().processModule(module);
+    assertProperties(module, p("name1", "value1"), p("name2", "value2"));
+  }
+
+  public void testProcessModuleForTestCaseWithoutProperties() {
+    ModuleDef module = new ModuleDef("myModule");
+    getStrategyForNoProperty().processModule(module);
+    assertProperties(module);
+  }
+
+  private void assertProperties(ModuleDef module, PropertyValue... props) {
+    SortedSet<BindingProperty> properties = module.getProperties().getBindingProperties();
+    assertEquals(props.length, properties.size());
+    int i = 0;
+    for (BindingProperty property : properties) {
+      assertEquals("property " + i, props[i].name, property.getName());
+      assertEquals("property " + i, props[i].value,
+          property.getConstrainedValue());
+      i++;
+    }
+  }
+
+  private static PropertyValue p(String name, String value) {
+    return new PropertyValue(name, value);
+  }
+
+  private PropertyDefiningStrategy getStrategyForSingleProperty() {
+    TestCase result = new PropertyDefiningStrategyTest();
+    result.setName("methodWithSingleProperty");
+    return new PropertyDefiningStrategy(result);
+  }
+
+  private PropertyDefiningStrategy getStrategyForTwoProperties() {
+    TestCase result = new PropertyDefiningStrategyTest();
+    result.setName("methodWithTwoProperties");
+    return new PropertyDefiningStrategy(result);
+  }
+
+  private PropertyDefiningStrategy getStrategyForNoProperty() {
+    TestCase result = new PropertyDefiningStrategyTest();
+    result.setName("methodWithoutProperties");
+    return new PropertyDefiningStrategy(result);
+  }
+}
diff --git a/user/test/com/google/gwt/junit/client/PropertyDefiningGWTTest.java b/user/test/com/google/gwt/junit/client/PropertyDefiningGWTTest.java
new file mode 100644
index 0000000..b311f39
--- /dev/null
+++ b/user/test/com/google/gwt/junit/client/PropertyDefiningGWTTest.java
@@ -0,0 +1,75 @@
+/*
+ * 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.client;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.i18n.client.DateTimeFormat;
+import com.google.gwt.junit.client.WithProperties.Property;
+
+import java.util.Date;
+
+/**
+ * A {@link GWTTestCase} that defines module properties.
+ */
+public class PropertyDefiningGWTTest extends GWTTestCase {
+  /**
+   * Base interface used for testing.
+   */
+  public static interface TestInterface {
+    String value();
+  }
+
+  /**
+   * Implementation used in one deferred binding.
+   */
+  public static class TestImplOne implements TestInterface {
+    public String value() {
+      return "one";
+    }
+  }
+
+  /**
+   * Implementation used in the other deferred binding.
+   */
+  public static class TestImplTwo implements TestInterface {
+    public String value() {
+      return "two";
+    }
+  }
+
+  @Override
+  public String getModuleName() {
+    return "com.google.gwt.junit.JUnitTestWithProperties";
+  }
+
+  @WithProperties({
+    @Property(name = "locale", value = "en_US"),
+    @Property(name = "my.property", value = "one")
+  })
+  public void testInUSLocaleAndPropertyOne() {
+    assertEquals("June", DateTimeFormat.getFormat("MMMM").format(new Date(99, 5, 13)));
+    assertEquals("one", GWT.<TestInterface> create(TestInterface.class).value());
+  }
+
+  @WithProperties({
+    @Property(name = "locale", value = "de_CH"),
+    @Property(name = "my.property", value = "two")
+  })
+  public void testInSwissLocaleAndPropertyTwo() {
+    assertEquals("Juni", DateTimeFormat.getFormat("MMMM").format(new Date(99, 5, 13)));
+    assertEquals("two", GWT.<TestInterface> create(TestInterface.class).value());
+  }
+}