Ant rework for speed:
* Added ant task LatestTimeJar to jar only the most 
recent of duplicate 
  entries.
* Used <outofdate> so jars in dev using <zipfileset> don't bother testing
  entries if the containing archive is unmodified.

Ant rework for cleanup:
* Introduced local.ant.properties
* Refactored test files containing $ to test_i18n_dollar, with companion
  test_i18n_bar, so we can test both sides of that split (and so it's easy
  for systems allergic to $ in filenames to opt out).  Ant property
  gwt.i18n.test.InnerClassChar={dollar,bar} selects which to use.
* Unified test target names to test.*, deprecating remoteweb-test and
  selenium-test as nonconforming.

Review by: scottb, jat



git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@5537 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/build-tools/ant-gwt/build.xml b/build-tools/ant-gwt/build.xml
index 68c30a7..81b02d6 100644
--- a/build-tools/ant-gwt/build.xml
+++ b/build-tools/ant-gwt/build.xml
@@ -24,10 +24,12 @@
 
   <target name="build" depends="compile" description="Packages this project into a jar">
     <mkdir dir="${gwt.build.lib}" />
-    <gwt.jar destfile="${gwt.build.lib}/${ant.project.name}.jar">
-      <fileset dir="src" />
+    <!-- we can't use the gwt.jar macro, as it needs the gwt-ant.jar being packaged here -->
+    <jar destfile="${gwt.build.lib}/${ant.project.name}.jar" duplicate="fail" filesonly="true" 
+         index="true" update="true">
       <fileset dir="${javac.out}" />
-    </gwt.jar>
+      <fileset dir="src" />
+    </jar>
   </target>
 
   <target name="test" depends="build, compile.tests" description="Run unit tests for this project.">
diff --git a/build-tools/ant-gwt/src/com/google/gwt/ant/taskdefs/LatestTimeJar.java b/build-tools/ant-gwt/src/com/google/gwt/ant/taskdefs/LatestTimeJar.java
new file mode 100644
index 0000000..7cf7352
--- /dev/null
+++ b/build-tools/ant-gwt/src/com/google/gwt/ant/taskdefs/LatestTimeJar.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright 2008 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.ant.taskdefs;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.Jar;
+import org.apache.tools.zip.ZipExtraField;
+import org.apache.tools.zip.ZipOutputStream;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Map;
+import java.util.TreeMap;
+
+/**
+ * A variation on Jar which handles duplicate entries by only archiving the most
+ * recent of any given path. This is done by keeping a map of paths (as shown in
+ * the jar file) against {@link #EntryInfo} objects identifying the input source
+ * and its timestamp. Most of the actual archiving is deferred until archive
+ * finalization, when we've decided on the actual de-duplicated set of entries.
+ */
+public class LatestTimeJar extends Jar {
+
+  /**
+   * Metadata about pending entries in the jar, for replacement if newer entries
+   * are found. Subclasses of EntryInfo are held in the
+   */
+  protected abstract class EntryInfo {
+    protected long timestamp;
+    protected int mode;
+
+    public EntryInfo(long lastModified, int mode) {
+      this.timestamp = lastModified;
+      this.mode = mode;
+    }
+
+    /**
+     * Called to actually add the entry to a given zip stream.
+     *
+     * @param out
+     * @param path
+     * @throws IOException
+     */
+    public abstract void addToZip(ZipOutputStream out, String path)
+        throws IOException;
+
+    public long getLastModified() {
+      return timestamp;
+    }
+
+    public int getMode() {
+      return mode;
+    }
+  }
+
+  /**
+   * Metadata about a directory entry.
+   */
+  protected class DirEntryInfo extends EntryInfo {
+    protected File dir;
+    protected ZipExtraField extra[];
+
+    public DirEntryInfo(File dir, long touchTime, int mode,
+        ZipExtraField extra[]) {
+      super(touchTime, mode);
+      this.dir = dir;
+      this.extra = extra;
+    }
+
+    @Override
+    public void addToZip(ZipOutputStream out, String path) throws IOException {
+      doZipDir(dir, out, path, mode, extra);
+    }
+  }
+
+  /**
+   * Metadata about a file entry.
+   */
+  protected class FileEntryInfo extends EntryInfo {
+    private File tmpFile;
+    private File archive;
+
+    public FileEntryInfo(InputStream in, long lastModified, File fromArchive,
+        int mode) throws IOException {
+      super(lastModified, mode);
+      tmpFile = File.createTempFile("gwtjar", "");
+      tmpFile.deleteOnExit();
+      OutputStream fos = new FileOutputStream(tmpFile);
+      int readLen = in.read(buffer);
+      while (readLen > 0) {
+        fos.write(buffer, 0, readLen);
+        readLen = in.read(buffer);
+      }
+      fos.close();
+      archive = fromArchive;
+    }
+
+    @Override
+    public void addToZip(ZipOutputStream out, String path) throws IOException {
+      FileInputStream inStream = new FileInputStream(tmpFile);
+      doZipFile(inStream, out, path, timestamp, archive, mode);
+      tmpFile.delete();
+    }
+  }
+
+  private byte buffer[] = new byte[16 * 1024];
+  private Map<String, EntryInfo> paths = new TreeMap<String, EntryInfo>();
+
+  @Override
+  protected void finalizeZipOutputStream(ZipOutputStream out)
+      throws IOException, BuildException {
+    for (String path : paths.keySet()) {
+      paths.get(path).addToZip(out, path);
+    }
+    super.finalizeZipOutputStream(out);
+  }
+
+  @Override
+  protected void zipDir(File dir,
+      @SuppressWarnings("unused") ZipOutputStream out, String path, int mode,
+      ZipExtraField[] extra) throws IOException {
+    long thisTouchTime = (dir == null ? 0L : dir.lastModified());
+    String dirName = (dir == null ? "<null>" : dir.getAbsolutePath());
+    if (shouldReplace(path, thisTouchTime)) {
+      if (paths.get(path) != null) {
+        log("Obsoleting older " + path + " with " + dirName,
+            Project.MSG_VERBOSE);
+      }
+      paths.put(path, new DirEntryInfo(dir, thisTouchTime, mode, extra));
+    } else {
+      log("Newer " + path + " already added, skipping " + dirName,
+          Project.MSG_VERBOSE);
+    }
+  }
+
+  @Override
+  protected void zipFile(InputStream in,
+      @SuppressWarnings("unused") ZipOutputStream out, String path,
+      long lastModified, File fromArchive, int mode) throws IOException {
+
+    String desc = (fromArchive == null ? "file" : "file from "
+        + fromArchive.getAbsolutePath());
+
+    if (shouldReplace(path, lastModified)) {
+      if (paths.get(path) != null) {
+        log("Obsoleting older " + path + " with " + desc, Project.MSG_VERBOSE);
+      }
+      paths.put(path, new FileEntryInfo(in, lastModified, fromArchive, mode));
+    } else {
+      log("Newer " + path + " already added, skipping " + desc,
+          Project.MSG_VERBOSE);
+    }
+  }
+
+  private void doZipDir(File dir, ZipOutputStream out, String entryName,
+      int mode, ZipExtraField[] extra) throws IOException {
+    super.zipDir(dir, out, entryName, mode, extra);
+  }
+
+  private void doZipFile(InputStream inStream, ZipOutputStream out,
+      String entryName, long timestamp, File archive, int mode)
+      throws IOException {
+    super.zipFile(inStream, out, entryName, timestamp, archive, mode);
+  }
+
+  /**
+   * Checks whether an entry should be replaced, by touch dates and duplicates
+   * setting.
+   *
+   * @param path the path of an entry being considered
+   * @param touchTime the lastModified of the candiate replacement
+   * @return
+   */
+  private boolean shouldReplace(String path, long touchTime) {
+    EntryInfo oldInfo = paths.get(path);
+    // adding from jars, we get directories with 0L time; missing should be
+    // earlier than that, -1L.
+    long existingTouchTime = ((oldInfo != null) ? oldInfo.getLastModified()
+        : -1L);
+    return (existingTouchTime < touchTime || (existingTouchTime == touchTime && this.duplicate.equals("add")));
+  }
+}
diff --git a/common.ant.xml b/common.ant.xml
index 358f879..9f4c423 100755
--- a/common.ant.xml
+++ b/common.ant.xml
@@ -1,4 +1,10 @@
 <project name="common">
+  <!-- it's okay for this not to exist, but it gives a place to store
+       "your" property settings, if any, persistently.  For example, you
+       might use it to set gwt.junit.testcase.includes to a narrower subset
+       of test cases to exercise. -->
+  <property file="local.ant.properties" />
+
   <!-- gwt.build.iscasesensitivefs is true if the filesystem of the
     build machine is case-sensitive, false otherwise.  Update with
     new lines for any supported platforms with case-insensitive
@@ -135,9 +141,22 @@
     <javac srcdir="src" destdir="${javac.out}" debug="${javac.debug}" debuglevel="${javac.debuglevel}" source="${javac.source}" target="${javac.target}" nowarn="${javac.nowarn}" encoding="${javac.encoding}" />
   </presetdef>
 
-  <presetdef name="gwt.jar">
-    <jar destfile="${project.lib}" update="true" duplicate="preserve" index="true" />
-  </presetdef>
+  <macrodef name="gwt.jar">
+    <attribute name="destfile" default="${project.lib}"/>
+    <attribute name="duplicate" default="fail"/>
+    <attribute name="update" default="true"/>
+    <element name="jarcontents" implicit="true"/>
+    <sequential>
+      <taskdef name="jar.bydate"
+          classname="com.google.gwt.ant.taskdefs.LatestTimeJar"
+          classpath="${gwt.build.lib}/ant-gwt.jar" />
+
+      <jar.bydate destfile="@{destfile}" duplicate="@{duplicate}" filesonly="false"
+          index="true" update="@{update}">
+        <jarcontents/>
+      </jar.bydate>
+    </sequential>
+  </macrodef>
 
   <macrodef name="gwt.junit">
     <!-- TODO: make this more generic / refactor so it can be used from dev/core -->
diff --git a/dev/common.ant.xml b/dev/common.ant.xml
index 4353204..ef64c7a 100755
--- a/dev/common.ant.xml
+++ b/dev/common.ant.xml
@@ -18,21 +18,40 @@
 
   <target name="build" depends="compile" description="Build and package this project">
     <mkdir dir="${gwt.build.lib}" />
-    <gwt.jar>
-      <fileset dir="src" excludes="**/package.html"/>
-      <fileset dir="${gwt.core.root}/src">
-        <exclude name="**/package.html"/>
-        <exclude name="com/google/gwt/dev/About.properties"/>
-      </fileset>
-      <fileset dir="${gwt.core.root}/super" excludes="**/package.html" />
-      <fileset dir="${javac.out}" />
-      <fileset dir="${gwt.core.build}/bin" />
-      <zipfileset src="${gwt.tools.lib}/eclipse/${gwt.dev.swt.jar}" />
-      <zipfileset src="${gwt.core.build}/alldeps.jar" />
-      <manifest>
-        <attribute name="Main-Class" value="com.google.gwt.dev.GWTMain" />
-      </manifest>
-    </gwt.jar>
+    <outofdate>
+      <sourcefiles>
+        <fileset dir="src" excludes="**/package.html"/>
+        <fileset dir="${gwt.core.root}/src">
+          <exclude name="**/package.html"/>
+          <exclude name="**/*.properties"/> <!-- copied and/or filtered into bin -->
+        </fileset>
+        <fileset dir="${gwt.core.root}/super" excludes="**/package.html" />
+        <fileset dir="${javac.out}" />
+        <fileset dir="${gwt.core.build}/bin" />
+        <fileset file="${gwt.tools.lib}/eclipse/${gwt.dev.swt.jar}" />
+        <fileset file="${gwt.core.build}/alldeps.jar" />
+      </sourcefiles>
+      <targetfiles>
+        <fileset file="${project.lib}"/>
+      </targetfiles>
+      <sequential>
+        <gwt.jar>
+          <fileset dir="src" excludes="**/package.html"/>
+          <fileset dir="${gwt.core.root}/src">
+            <exclude name="**/package.html"/>
+            <exclude name="**/*.properties"/> <!-- copied and/or filtered into bin -->
+          </fileset>
+          <fileset dir="${gwt.core.root}/super" excludes="**/package.html" />
+          <fileset dir="${javac.out}" />
+          <fileset dir="${gwt.core.build}/bin" />
+          <zipfileset src="${gwt.tools.lib}/eclipse/${gwt.dev.swt.jar}" />
+          <zipfileset src="${gwt.core.build}/alldeps.jar" />
+          <manifest>
+            <attribute name="Main-Class" value="com.google.gwt.dev.GWTMain" />
+          </manifest>
+        </gwt.jar>
+      </sequential>
+    </outofdate>
   </target>
 
   <target name="clean" description="Cleans this project's intermediate and output files">
diff --git a/dev/core/build.xml b/dev/core/build.xml
index 5f4876a..e868ed4 100755
--- a/dev/core/build.xml
+++ b/dev/core/build.xml
@@ -22,39 +22,82 @@
 
   <target name="build.alldeps.jar" description="Merges all dependency jars into a single jar">
     <mkdir dir="${project.build}" />
-    <gwt.jar destfile="${alldeps.jar}">
-      <zipfileset src="${gwt.tools.lib}/apache/tapestry-util-text-4.0.2.jar" />
-      <zipfileset src="${gwt.tools.lib}/apache/ant-1.6.5.jar" />
-      <zipfileset src="${gwt.tools.lib}/eclipse/jdt-3.4.2.jar" />
-      <zipfileset src="${gwt.tools.lib}/jetty/jetty-6.1.11.jar" />
-      <zipfileset src="${gwt.tools.lib}/tomcat/ant-launcher-1.6.5.jar" />
-      <zipfileset src="${gwt.tools.lib}/tomcat/catalina-1.0.jar" />
-      <zipfileset src="${gwt.tools.lib}/tomcat/catalina-optional-1.0.jar" />
-      <zipfileset src="${gwt.tools.lib}/tomcat/commons-beanutils-1.6.jar" />
-      <zipfileset src="${gwt.tools.lib}/tomcat/commons-collections-3.1.jar" />
-      <zipfileset src="${gwt.tools.lib}/tomcat/commons-digester-1.5.jar" />
-      <zipfileset src="${gwt.tools.lib}/tomcat/commons-el-1.0.jar" />
-      <zipfileset src="${gwt.tools.lib}/tomcat/commons-logging-1.0.jar" />
-      <zipfileset src="${gwt.tools.lib}/tomcat/commons-modeler-1.1.jar" />
-      <zipfileset src="${gwt.tools.lib}/tomcat/jakarta-regexp-1.3.jar" />
-      <zipfileset src="${gwt.tools.lib}/tomcat/jasper-compiler-1.0.jar" />
-      <zipfileset src="${gwt.tools.lib}/tomcat/jasper-runtime-1.0.jar" />
-      <zipfileset src="${gwt.tools.lib}/tomcat/jsp-api-2.0.jar" />
-      <zipfileset src="${gwt.tools.lib}/tomcat/mx4j-jmx-1.1.jar" />
-      <zipfileset src="${gwt.tools.lib}/tomcat/naming-common-1.0.jar" />
-      <zipfileset src="${gwt.tools.lib}/tomcat/naming-factory-1.0.jar" />
-      <zipfileset src="${gwt.tools.lib}/tomcat/naming-java-1.0.jar" />
-      <zipfileset src="${gwt.tools.lib}/tomcat/naming-resources-1.0.jar" />
-      <zipfileset src="${gwt.tools.lib}/tomcat/servlet-api-2.5.jar" />
-      <zipfileset src="${gwt.tools.lib}/tomcat/servlet-api-2.4.jar" />
-      <zipfileset src="${gwt.tools.lib}/tomcat/servlets-common-1.0.jar" />
-      <zipfileset src="${gwt.tools.lib}/tomcat/servlets-default-1.0.jar" />
-      <zipfileset src="${gwt.tools.lib}/tomcat/servlets-invoker-1.0.jar" />
-      <zipfileset src="${gwt.tools.lib}/tomcat/tomcat-coyote-1.0.jar" />
-      <zipfileset src="${gwt.tools.lib}/tomcat/tomcat-http11-1.0.jar" />
-      <zipfileset src="${gwt.tools.lib}/tomcat/tomcat-jk2-2.1.jar" />
-      <zipfileset src="${gwt.tools.lib}/tomcat/tomcat-util-5.1.jar" />
-    </gwt.jar>
+    <outofdate> <!-- saves rescanning all the source jars' contents -->
+      <sourcefiles>
+        <fileset dir="${gwt.tools.lib}">
+          <include name="apache/tapestry-util-text-4.0.2.jar" />
+          <include name="apache/ant-1.6.5.jar" />
+          <include name="eclipse/jdt-3.4.2.jar" />
+          <include name="jetty/jetty-6.1.11.jar" />
+          <include name="tomcat/ant-launcher-1.6.5.jar" />
+          <include name="tomcat/catalina-1.0.jar" />
+          <include name="tomcat/catalina-optional-1.0.jar" />
+          <include name="tomcat/commons-beanutils-1.6.jar" />
+          <include name="tomcat/commons-collections-3.1.jar" />
+          <include name="tomcat/commons-digester-1.5.jar" />
+          <include name="tomcat/commons-el-1.0.jar" />
+          <include name="tomcat/commons-logging-1.0.jar" />
+          <include name="tomcat/commons-modeler-1.1.jar" />
+          <include name="tomcat/jakarta-regexp-1.3.jar" />
+          <include name="tomcat/jasper-compiler-1.0.jar" />
+          <include name="tomcat/jasper-runtime-1.0.jar" />
+          <include name="tomcat/jsp-api-2.0.jar" />
+          <include name="tomcat/mx4j-jmx-1.1.jar" />
+          <include name="tomcat/naming-common-1.0.jar" />
+          <include name="tomcat/naming-factory-1.0.jar" />
+          <include name="tomcat/naming-java-1.0.jar" />
+          <include name="tomcat/naming-resources-1.0.jar" />
+          <include name="tomcat/servlet-api-2.5.jar" />
+          <include name="tomcat/servlet-api-2.4.jar" />
+          <include name="tomcat/servlets-common-1.0.jar" />
+          <include name="tomcat/servlets-default-1.0.jar" />
+          <include name="tomcat/servlets-invoker-1.0.jar" />
+          <include name="tomcat/tomcat-coyote-1.0.jar" />
+          <include name="tomcat/tomcat-http11-1.0.jar" />
+          <include name="tomcat/tomcat-jk2-2.1.jar" />
+          <include name="tomcat/tomcat-util-5.1.jar" />
+        </fileset>
+        <fileset file="build.xml"/>
+      </sourcefiles>
+      <targetfiles>
+        <fileset file="${alldeps.jar}"/>
+      </targetfiles>
+      <sequential>
+        <gwt.jar destfile="${alldeps.jar}">
+          <zipfileset src="${gwt.tools.lib}/apache/tapestry-util-text-4.0.2.jar" />
+          <zipfileset src="${gwt.tools.lib}/apache/ant-1.6.5.jar" />
+          <zipfileset src="${gwt.tools.lib}/eclipse/jdt-3.4.2.jar" />
+          <zipfileset src="${gwt.tools.lib}/jetty/jetty-6.1.11.jar" />
+          <zipfileset src="${gwt.tools.lib}/tomcat/ant-launcher-1.6.5.jar" />
+          <zipfileset src="${gwt.tools.lib}/tomcat/catalina-1.0.jar" />
+          <zipfileset src="${gwt.tools.lib}/tomcat/catalina-optional-1.0.jar" />
+          <zipfileset src="${gwt.tools.lib}/tomcat/commons-beanutils-1.6.jar" />
+          <zipfileset src="${gwt.tools.lib}/tomcat/commons-collections-3.1.jar" />
+          <zipfileset src="${gwt.tools.lib}/tomcat/commons-digester-1.5.jar" />
+          <zipfileset src="${gwt.tools.lib}/tomcat/commons-el-1.0.jar" />
+          <zipfileset src="${gwt.tools.lib}/tomcat/commons-logging-1.0.jar" />
+          <zipfileset src="${gwt.tools.lib}/tomcat/commons-modeler-1.1.jar" />
+          <zipfileset src="${gwt.tools.lib}/tomcat/jakarta-regexp-1.3.jar" />
+          <zipfileset src="${gwt.tools.lib}/tomcat/jasper-compiler-1.0.jar" />
+          <zipfileset src="${gwt.tools.lib}/tomcat/jasper-runtime-1.0.jar" />
+          <zipfileset src="${gwt.tools.lib}/tomcat/jsp-api-2.0.jar" />
+          <zipfileset src="${gwt.tools.lib}/tomcat/mx4j-jmx-1.1.jar" />
+          <zipfileset src="${gwt.tools.lib}/tomcat/naming-common-1.0.jar" />
+          <zipfileset src="${gwt.tools.lib}/tomcat/naming-factory-1.0.jar" />
+          <zipfileset src="${gwt.tools.lib}/tomcat/naming-java-1.0.jar" />
+          <zipfileset src="${gwt.tools.lib}/tomcat/naming-resources-1.0.jar" />
+          <zipfileset src="${gwt.tools.lib}/tomcat/servlet-api-2.5.jar" />
+          <zipfileset src="${gwt.tools.lib}/tomcat/servlet-api-2.4.jar" />
+          <zipfileset src="${gwt.tools.lib}/tomcat/servlets-common-1.0.jar" />
+          <zipfileset src="${gwt.tools.lib}/tomcat/servlets-default-1.0.jar" />
+          <zipfileset src="${gwt.tools.lib}/tomcat/servlets-invoker-1.0.jar" />
+          <zipfileset src="${gwt.tools.lib}/tomcat/tomcat-coyote-1.0.jar" />
+          <zipfileset src="${gwt.tools.lib}/tomcat/tomcat-http11-1.0.jar" />
+          <zipfileset src="${gwt.tools.lib}/tomcat/tomcat-jk2-2.1.jar" />
+          <zipfileset src="${gwt.tools.lib}/tomcat/tomcat-util-5.1.jar" />
+        </gwt.jar>
+      </sequential>
+    </outofdate>
   </target>
 
   <property name="filter.pattern" value="com/google/gwt/dev/About.properties" />
diff --git a/user/build.xml b/user/build.xml
index 2511253..d3cae6f 100755
--- a/user/build.xml
+++ b/user/build.xml
@@ -2,9 +2,17 @@
   <property name="gwt.root" location=".." />
   <property name="project.tail" value="user" />
   <property name="test.args" value="" />
-  <property name="gwt.junit.emmatestcase.includes" value="**/*Suite.class,com/google/gwt/dev/jjs/test/*Test.class"/>
+
   <import file="${gwt.root}/common.ant.xml" />
 
+  <property name="gwt.junit.emmatestcase.includes" value="**/*Suite.class,com/google/gwt/dev/jjs/test/*Test.class"/>
+
+  <!-- 
+    Whether I18NSuite should test e.g. Foo$InnerMsgs_fr.properties (if the
+    value is "dollar") or Foo_Inner_fr.properties (for "bar")
+   -->
+  <property name="gwt.i18n.test.InnerClassChar" value="dollar"/>
+
   <!--
     Default hosted mode test cases
   -->
@@ -27,6 +35,14 @@
   <fileset id="default.web.tests" dir="${javac.junit.out}"
          includes="${gwt.junit.testcase.includes}" />
 
+  <!--
+    Classpaths added for test cases
+  -->
+  <path id="test.extraclasspath">
+    <pathelement location="${gwt.build}/out/dev/core/bin-test" />
+    <pathelement location="test_i18n_${gwt.i18n.test.InnerClassChar}" />
+  </path>
+
   <!-- Platform shouldn't matter here, just picking one -->
   <property.ensure name="gwt.dev.jar" location="${gwt.build.lib}/gwt-dev-linux.jar" />
 
@@ -91,20 +107,28 @@
     </gwt.checkstyle>
   </target>
 
-  <target name="remoteweb-test" description="Run a remoteweb test at the given host and path" if="gwt.remote.browsers">
+  <target name="remoteweb-test" depends="test.remoteweb">
+    <echo message="DEPRECATED: remoteweb-test has been renamed test.remoteweb"/>
+  </target>
+
+  <target name="test.remoteweb" description="Run a remoteweb test at the given host and path" if="gwt.remote.browsers">
     <echo message="Performing remote browser testing at ${gwt.remote.browsers}" />
     <gwt.junit test.args="${test.args} -out www -remoteweb ${gwt.remote.browsers}" test.out="${junit.out}/remoteweb" test.cases="default.web.tests" >
       <extraclasspaths>
-        <pathelement location="${gwt.build}/out/dev/core/bin-test" />
+        <path refid="test.extraclasspath" />
       </extraclasspaths>
     </gwt.junit>
   </target>
 
-  <target name="selenium-test" description="Run a remote test using Selenium RC test at the given host and path" if="gwt.selenium.hosts">
+  <target name="selenium-test" depends="test.selenium">
+    <echo message="DEPRECATED: selenium-test has been renamed test.selenium"/>
+  </target>
+
+  <target name="test.selenium" description="Run a remote test using Selenium RC test at the given host and path" if="gwt.selenium.hosts">
     <echo message="Performing remote browser testing using Selenium RC at ${gwt.selenium.hosts}" />
     <gwt.junit test.args="${test.args} -out www -selenium ${gwt.selenium.hosts}" test.out="${junit.out}/selenium" test.cases="default.web.tests" >
       <extraclasspaths>
-        <pathelement location="${gwt.build}/out/dev/core/bin-test" />
+        <path refid="test.extraclasspath" />
       </extraclasspaths>
     </gwt.junit>
   </target>
@@ -112,7 +136,7 @@
   <target name="test.hosted.emma" depends="compile, compile.tests" description="Run all hosted-mode tests in emma mode.">
     <gwt.junit test.args="${test.args}" test.out="${junit.out}/${build.host.platform}-hosted-mode-emma" test.cases="default.hosted.emma.tests" >
       <extraclasspaths>
-        <pathelement location="${gwt.build}/out/dev/core/bin-test" />
+        <path refid="test.extraclasspath" />
         <pathelement location="${gwt.tools.redist}/emma/emma.jar" />
       </extraclasspaths>
     </gwt.junit>
@@ -121,7 +145,7 @@
   <target name="test.hosted" depends="compile, compile.tests" description="Run only hosted-mode tests for this project.">
     <gwt.junit test.args="${test.args}" test.out="${junit.out}/${build.host.platform}-hosted-mode" test.cases="default.hosted.tests" >
       <extraclasspaths>
-        <pathelement location="${gwt.build}/out/dev/core/bin-test" />
+        <path refid="test.extraclasspath" />
       </extraclasspaths>
     </gwt.junit>
   </target>
@@ -129,7 +153,7 @@
   <target name="test.noserver" depends="compile, compile.tests" description="Run noserver hosted-mode tests for this project.">
     <gwt.junit test.args="${test.args}" test.out="${junit.out}/${build.host.platform}-noserver-mode" test.cases="default.noserver.tests">
       <extraclasspaths>
-        <pathelement location="${gwt.build}/out/dev/core/bin-test" />
+        <path refid="test.extraclasspath" />
       </extraclasspaths>
     </gwt.junit>
   </target>
@@ -137,7 +161,7 @@
   <target name="test.web" depends="compile, compile.tests" description="Run only web-mode tests for this project.">
     <gwt.junit test.args="${test.args} -out www -web" test.out="${junit.out}/${build.host.platform}-web-mode" test.cases="default.web.tests">
       <extraclasspaths>
-        <pathelement location="${gwt.build}/out/dev/core/bin-test" />
+        <path refid="test.extraclasspath" />
       </extraclasspaths>
     </gwt.junit>
   </target>
@@ -145,7 +169,7 @@
   <target name="test.web.disableClassMetadata" depends="compile, compile.tests" description="Run only web-mode tests for this project.">
     <gwt.junit test.args="${test.args} -XdisableClassMetadata -out www -web" test.out="${junit.out}/${build.host.platform}-web-mode-disableClassMetadata" test.cases="default.web.tests" >
       <extraclasspaths>
-        <pathelement location="${gwt.build}/out/dev/core/bin-test" />
+        <path refid="test.extraclasspath" />
       </extraclasspaths>
     </gwt.junit>
   </target>
@@ -153,7 +177,7 @@
   <target name="test.web.draft" depends="compile, compile.tests" description="Run only web-mode tests for this project.">
     <gwt.junit test.args="${test.args} -draftCompile -out www -web" test.out="${junit.out}/${build.host.platform}-web-mode-draft" test.cases="default.web.tests" >
       <extraclasspaths>
-        <pathelement location="${gwt.build}/out/dev/core/bin-test" />
+        <path refid="test.extraclasspath" />
       </extraclasspaths>
     </gwt.junit>
   </target>
@@ -168,9 +192,9 @@
     <limit failonerror="true" hours="3">
     <parallel threadsPerProcessor="${gwt.threadsPerProcessor}">
       <!-- selenium-test is a no-op unless gwt.selenium.hosts is defined -->
-      <antcall target="selenium-test"/>
+      <antcall target="test.selenium"/>
       <!-- remoteweb-test is a no-op unless gwt.remote.browsers is defined -->
-      <antcall target="remoteweb-test"/>
+      <antcall target="test.remoteweb"/>
       <antcall target="test.hosted"/>
       <antcall target="test.hosted.emma"/>
       <antcall target="test.noserver"/>
diff --git a/user/test/com/google/gwt/i18n/client/TestAnnotatedMessages$Nested_bb_CC.properties b/user/test_i18n_bar/com/google/gwt/i18n/client/TestAnnotatedMessages_Nested_bb_CC.properties
similarity index 100%
copy from user/test/com/google/gwt/i18n/client/TestAnnotatedMessages$Nested_bb_CC.properties
copy to user/test_i18n_bar/com/google/gwt/i18n/client/TestAnnotatedMessages_Nested_bb_CC.properties
diff --git a/user/test/com/google/gwt/i18n/client/resolutiontest/Inners$ExtendsInnerInner.properties b/user/test_i18n_bar/com/google/gwt/i18n/client/resolutiontest/Inners_ExtendsInnerInner.properties
similarity index 100%
copy from user/test/com/google/gwt/i18n/client/resolutiontest/Inners$ExtendsInnerInner.properties
copy to user/test_i18n_bar/com/google/gwt/i18n/client/resolutiontest/Inners_ExtendsInnerInner.properties
diff --git a/user/test/com/google/gwt/i18n/client/resolutiontest/Inners$HasInner.properties b/user/test_i18n_bar/com/google/gwt/i18n/client/resolutiontest/Inners_HasInner.properties
similarity index 100%
copy from user/test/com/google/gwt/i18n/client/resolutiontest/Inners$HasInner.properties
copy to user/test_i18n_bar/com/google/gwt/i18n/client/resolutiontest/Inners_HasInner.properties
diff --git a/user/test/com/google/gwt/i18n/client/resolutiontest/Inners$HasInner$IsInner.properties b/user/test_i18n_bar/com/google/gwt/i18n/client/resolutiontest/Inners_HasInner_IsInner.properties
similarity index 100%
copy from user/test/com/google/gwt/i18n/client/resolutiontest/Inners$HasInner$IsInner.properties
copy to user/test_i18n_bar/com/google/gwt/i18n/client/resolutiontest/Inners_HasInner_IsInner.properties
diff --git a/user/test/com/google/gwt/i18n/client/resolutiontest/Inners$InnerClass$InnerInner.properties b/user/test_i18n_bar/com/google/gwt/i18n/client/resolutiontest/Inners_InnerClass_InnerInner.properties
similarity index 100%
copy from user/test/com/google/gwt/i18n/client/resolutiontest/Inners$InnerClass$InnerInner.properties
copy to user/test_i18n_bar/com/google/gwt/i18n/client/resolutiontest/Inners_InnerClass_InnerInner.properties
diff --git a/user/test/com/google/gwt/i18n/client/resolutiontest/Inners$InnerClass$InnerInnerMessages.properties b/user/test_i18n_bar/com/google/gwt/i18n/client/resolutiontest/Inners_InnerClass_InnerInnerMessages.properties
similarity index 100%
copy from user/test/com/google/gwt/i18n/client/resolutiontest/Inners$InnerClass$InnerInnerMessages.properties
copy to user/test_i18n_bar/com/google/gwt/i18n/client/resolutiontest/Inners_InnerClass_InnerInnerMessages.properties
diff --git a/user/test/com/google/gwt/i18n/client/resolutiontest/Inners$InnerClass$ProtectedInnerInnerClass$ExtendsAnotherInner.properties b/user/test_i18n_bar/com/google/gwt/i18n/client/resolutiontest/Inners_InnerClass_ProtectedInnerInnerClass_ExtendsAnotherInner.properties
similarity index 100%
copy from user/test/com/google/gwt/i18n/client/resolutiontest/Inners$InnerClass$ProtectedInnerInnerClass$ExtendsAnotherInner.properties
copy to user/test_i18n_bar/com/google/gwt/i18n/client/resolutiontest/Inners_InnerClass_ProtectedInnerInnerClass_ExtendsAnotherInner.properties
diff --git a/user/test/com/google/gwt/i18n/client/resolutiontest/Inners$InnerClass$ProtectedInnerInnerClass$ExtendsAnotherInner$ExtendProtectedInner.properties b/user/test_i18n_bar/com/google/gwt/i18n/client/resolutiontest/Inners_InnerClass_ProtectedInnerInnerClass_ExtendsAnotherInner_ExtendProtectedInner.properties
similarity index 100%
copy from user/test/com/google/gwt/i18n/client/resolutiontest/Inners$InnerClass$ProtectedInnerInnerClass$ExtendsAnotherInner$ExtendProtectedInner.properties
copy to user/test_i18n_bar/com/google/gwt/i18n/client/resolutiontest/Inners_InnerClass_ProtectedInnerInnerClass_ExtendsAnotherInner_ExtendProtectedInner.properties
diff --git a/user/test/com/google/gwt/i18n/client/resolutiontest/Inners$ProtectedInner.properties b/user/test_i18n_bar/com/google/gwt/i18n/client/resolutiontest/Inners_ProtectedInner.properties
similarity index 100%
copy from user/test/com/google/gwt/i18n/client/resolutiontest/Inners$ProtectedInner.properties
copy to user/test_i18n_bar/com/google/gwt/i18n/client/resolutiontest/Inners_ProtectedInner.properties
diff --git a/user/test/com/google/gwt/i18n/client/resolutiontest/Inners$SimpleInner.properties b/user/test_i18n_bar/com/google/gwt/i18n/client/resolutiontest/Inners_SimpleInner.properties
similarity index 100%
copy from user/test/com/google/gwt/i18n/client/resolutiontest/Inners$SimpleInner.properties
copy to user/test_i18n_bar/com/google/gwt/i18n/client/resolutiontest/Inners_SimpleInner.properties
diff --git a/user/test/com/google/gwt/i18n/client/TestAnnotatedMessages$Nested_bb_CC.properties b/user/test_i18n_dollar/com/google/gwt/i18n/client/TestAnnotatedMessages$Nested_bb_CC.properties
similarity index 100%
rename from user/test/com/google/gwt/i18n/client/TestAnnotatedMessages$Nested_bb_CC.properties
rename to user/test_i18n_dollar/com/google/gwt/i18n/client/TestAnnotatedMessages$Nested_bb_CC.properties
diff --git a/user/test/com/google/gwt/i18n/client/resolutiontest/Inners$ExtendsInnerInner.properties b/user/test_i18n_dollar/com/google/gwt/i18n/client/resolutiontest/Inners$ExtendsInnerInner.properties
similarity index 100%
rename from user/test/com/google/gwt/i18n/client/resolutiontest/Inners$ExtendsInnerInner.properties
rename to user/test_i18n_dollar/com/google/gwt/i18n/client/resolutiontest/Inners$ExtendsInnerInner.properties
diff --git a/user/test/com/google/gwt/i18n/client/resolutiontest/Inners$HasInner$IsInner.properties b/user/test_i18n_dollar/com/google/gwt/i18n/client/resolutiontest/Inners$HasInner$IsInner.properties
similarity index 100%
rename from user/test/com/google/gwt/i18n/client/resolutiontest/Inners$HasInner$IsInner.properties
rename to user/test_i18n_dollar/com/google/gwt/i18n/client/resolutiontest/Inners$HasInner$IsInner.properties
diff --git a/user/test/com/google/gwt/i18n/client/resolutiontest/Inners$HasInner.properties b/user/test_i18n_dollar/com/google/gwt/i18n/client/resolutiontest/Inners$HasInner.properties
similarity index 100%
rename from user/test/com/google/gwt/i18n/client/resolutiontest/Inners$HasInner.properties
rename to user/test_i18n_dollar/com/google/gwt/i18n/client/resolutiontest/Inners$HasInner.properties
diff --git a/user/test/com/google/gwt/i18n/client/resolutiontest/Inners$InnerClass$InnerInner.properties b/user/test_i18n_dollar/com/google/gwt/i18n/client/resolutiontest/Inners$InnerClass$InnerInner.properties
similarity index 100%
rename from user/test/com/google/gwt/i18n/client/resolutiontest/Inners$InnerClass$InnerInner.properties
rename to user/test_i18n_dollar/com/google/gwt/i18n/client/resolutiontest/Inners$InnerClass$InnerInner.properties
diff --git a/user/test/com/google/gwt/i18n/client/resolutiontest/Inners$InnerClass$InnerInnerMessages.properties b/user/test_i18n_dollar/com/google/gwt/i18n/client/resolutiontest/Inners$InnerClass$InnerInnerMessages.properties
similarity index 100%
rename from user/test/com/google/gwt/i18n/client/resolutiontest/Inners$InnerClass$InnerInnerMessages.properties
rename to user/test_i18n_dollar/com/google/gwt/i18n/client/resolutiontest/Inners$InnerClass$InnerInnerMessages.properties
diff --git a/user/test/com/google/gwt/i18n/client/resolutiontest/Inners$InnerClass$ProtectedInnerInnerClass$ExtendsAnotherInner$ExtendProtectedInner.properties b/user/test_i18n_dollar/com/google/gwt/i18n/client/resolutiontest/Inners$InnerClass$ProtectedInnerInnerClass$ExtendsAnotherInner$ExtendProtectedInner.properties
similarity index 100%
rename from user/test/com/google/gwt/i18n/client/resolutiontest/Inners$InnerClass$ProtectedInnerInnerClass$ExtendsAnotherInner$ExtendProtectedInner.properties
rename to user/test_i18n_dollar/com/google/gwt/i18n/client/resolutiontest/Inners$InnerClass$ProtectedInnerInnerClass$ExtendsAnotherInner$ExtendProtectedInner.properties
diff --git a/user/test/com/google/gwt/i18n/client/resolutiontest/Inners$InnerClass$ProtectedInnerInnerClass$ExtendsAnotherInner.properties b/user/test_i18n_dollar/com/google/gwt/i18n/client/resolutiontest/Inners$InnerClass$ProtectedInnerInnerClass$ExtendsAnotherInner.properties
similarity index 100%
rename from user/test/com/google/gwt/i18n/client/resolutiontest/Inners$InnerClass$ProtectedInnerInnerClass$ExtendsAnotherInner.properties
rename to user/test_i18n_dollar/com/google/gwt/i18n/client/resolutiontest/Inners$InnerClass$ProtectedInnerInnerClass$ExtendsAnotherInner.properties
diff --git a/user/test/com/google/gwt/i18n/client/resolutiontest/Inners$ProtectedInner.properties b/user/test_i18n_dollar/com/google/gwt/i18n/client/resolutiontest/Inners$ProtectedInner.properties
similarity index 100%
rename from user/test/com/google/gwt/i18n/client/resolutiontest/Inners$ProtectedInner.properties
rename to user/test_i18n_dollar/com/google/gwt/i18n/client/resolutiontest/Inners$ProtectedInner.properties
diff --git a/user/test/com/google/gwt/i18n/client/resolutiontest/Inners$SimpleInner.properties b/user/test_i18n_dollar/com/google/gwt/i18n/client/resolutiontest/Inners$SimpleInner.properties
similarity index 100%
rename from user/test/com/google/gwt/i18n/client/resolutiontest/Inners$SimpleInner.properties
rename to user/test_i18n_dollar/com/google/gwt/i18n/client/resolutiontest/Inners$SimpleInner.properties