- Updates benchmarking to support annotations and
deprecate the existing metadata attributes. 
- Includes some existing benchmarks.
- Adds a new BenchmarkTest.

Patch by: tobyr
Review by: jat
Issue: 2045



git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@1753 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/javadoc/com/google/gwt/examples/benchmarks/AllocBenchmark.java b/user/javadoc/com/google/gwt/examples/benchmarks/AllocBenchmark.java
index 54e4c58..208e52a 100644
--- a/user/javadoc/com/google/gwt/examples/benchmarks/AllocBenchmark.java
+++ b/user/javadoc/com/google/gwt/examples/benchmarks/AllocBenchmark.java
@@ -15,27 +15,15 @@
  */
 package com.google.gwt.examples.benchmarks;
 
-import com.google.gwt.junit.client.Category;
 import com.google.gwt.junit.client.Benchmark;
 
 /**
  * Provides profile statistics on allocation times for different kinds of
  * objects.
  *
- * @gwt.benchmark.category com.google.gwt.user.client.ui.AllocBenchmark.AllocCategory
- *
  */
 public class AllocBenchmark extends Benchmark {
 
-  /**
-   * @gwt.benchmark.name Allocation Benchmarks
-   * @gwt.benchmark.description A series of benchmarks that tests the impact of
-   * different kinds of allocations.
-   *
-   */
-  class AllocCategory implements Category {
-  }
-
   private static final int numAllocs = 1000;
 
   public String getModuleName() {
diff --git a/user/javadoc/com/google/gwt/examples/benchmarks/ArrayListBenchmark.java b/user/javadoc/com/google/gwt/examples/benchmarks/ArrayListBenchmark.java
new file mode 100644
index 0000000..c9609ff
--- /dev/null
+++ b/user/javadoc/com/google/gwt/examples/benchmarks/ArrayListBenchmark.java
@@ -0,0 +1,226 @@
+/*
+ * 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.examples.benchmarks;
+import com.google.gwt.junit.client.IntRange;
+import com.google.gwt.junit.client.Benchmark;
+import com.google.gwt.junit.client.Operator;
+import com.google.gwt.junit.client.annotations.RangeField;
+import com.google.gwt.junit.client.annotations.RangeEnum;
+import com.google.gwt.junit.client.annotations.Setup;
+
+import java.util.List;
+import java.util.Arrays;
+import java.util.ArrayList;
+
+/**
+ * Benchmarks common operations on {@link List Lists}. This test covers
+ * appends, inserts, and removes for various sizes and positions.
+ */
+public class ArrayListBenchmark extends Benchmark {
+
+  private static final int PRIME = 3001;
+
+  /**
+   * The various positions that data can be inserted into a list.
+   */
+  protected enum Position {
+
+    BEGIN("at the beginning"),
+    EXPLICIT_END("explicitly at the end"),
+    IMPLICIT_END("implicitly at the end"),
+    VARIED("in varied locations");
+
+    private String label;
+
+    /**
+     * Constructor for <code>Position</code>.
+     *
+     * @param label a not <code>null</code> label describing this
+     * <code>Position</code>.
+     */
+    Position(String label) {
+      this.label = label;
+    }
+
+    /**
+     * Returns the textual description for the position.
+     *
+     * @return a not <code>null</code> description.
+     */
+    public String toString() {
+      return label;
+    }
+  }
+
+  protected final List<Position> explicitPositions = Arrays
+      .asList(Position.BEGIN, Position.EXPLICIT_END, Position.VARIED);
+
+  protected final IntRange insertRemoveRange = new IntRange(64,
+      Integer.MAX_VALUE, Operator.MULTIPLY, 2);
+
+  protected final IntRange baseRange = new IntRange(512, Integer.MAX_VALUE,
+      Operator.MULTIPLY, 2);
+
+  List<String> list;
+
+  int index = 0;
+
+  public String getModuleName() {
+    return "com.google.gwt.emultest.EmulSuite";
+  }
+
+  /**
+   * Appends <code>size</code> items to an empty {@code List}.
+   *
+   * @param size the size of the {@code List}
+   */
+  @Setup("beginListAdds")
+  public void testListAdds(@RangeField("baseRange") Integer size) {
+    int num = size.intValue();
+    for (int i = 0; i < num; i++) {
+      list.add("hello");
+    }
+  }
+
+  // Required for JUnit
+  public void testListAdds() {
+  }
+
+  /**
+   * Performs <code>size</code> gets on a {@code List} of size,
+   * <code>size</code>.
+   *
+   * @param size the size of the {@code List}
+   */
+  @Setup("beginListGets")
+  public void testListGets(@RangeField("baseRange") Integer size) {
+    int num = size.intValue();
+    for (int i = 0; i < num; i++) {
+      list.get(i);
+    }
+  }
+
+  // Required for JUnit
+  public void testListGets() {
+  }
+
+  /**
+   * Performs <code>size</code> inserts at position, <code>where</code>, on an
+   * empty <code>List</code>.
+   *
+   * @param where Where the inserts happen
+   * @param size The size of the <code>List</code>
+   *
+   */
+  @Setup("beginListInserts")
+  public void testListInserts(
+      @RangeEnum(Position.class)Position where,
+      @RangeField("insertRemoveRange")Integer size) {
+    insertIntoCollection(size, where, list);
+  }
+
+  // Required for JUnit
+  public void testListInserts() {
+  }
+
+  /**
+   * Performs <code>size</code> removes at position, <code>where</code>, on an
+   * ArrayList of size, <code>size</code>.
+   *
+   * @param where Where the inserts happen
+   * @param size The size of the <code>List</code>
+   */
+  @Setup("beginListRemoves")
+  public void testListRemoves(
+      @RangeField("explicitPositions")Position where,
+      @RangeField("insertRemoveRange")Integer size) {
+    removeFromCollection(size, where, list);
+  }
+
+  // Required for JUnit
+  public void testListRemoves() {
+  }
+
+  /**
+   * Creates a new empty List.
+   *
+   * @return a not <code>null</code>, empty List
+   */
+  protected List<String> newList() {
+    return new ArrayList<String>();
+  }
+
+  void beginListAdds(Integer size) {
+    list = newList();
+  }
+
+  void beginListGets(Integer size) {
+    createList(size);
+  }
+
+  void beginListInserts(Position where, Integer size) {
+    list = newList();
+    index = 0;
+  }
+
+  void beginListRemoves(Position where, Integer size) {
+    beginListInserts(where, size);
+    testListInserts(where, size);
+  }
+
+  private void createList(Integer size) {
+    beginListAdds(size);
+    testListAdds(size);
+  }
+
+  private void insertIntoCollection(Integer size, Position where, List<String> l) {
+    int num = size.intValue();
+    for (int i = 0; i < num; i++) {
+      if (where == Position.IMPLICIT_END) {
+        l.add("hello");
+      } else if (where == Position.BEGIN) {
+        l.add(0, "hello");
+      } else if (where == Position.EXPLICIT_END) {
+        l.add(l.size(), "hello");
+      } else if (where == Position.VARIED) {
+        l.add(index, "hello");
+        index += PRIME;
+        index %= l.size();
+      }
+    }
+  }
+
+  private int removeFromCollection(Integer size, Position where, List<String> l) {
+    int num = size.intValue();
+    for (int i = 0; i < num; i++) {
+      if (where == Position.IMPLICIT_END) {
+        throw new RuntimeException("cannot remove from the end implicitly");
+      } else if (where == Position.BEGIN) {
+        l.remove(0);
+      } else if (where == Position.EXPLICIT_END) {
+        l.remove(l.size() - 1);
+      } else if (where == Position.VARIED) {
+        l.remove(index);
+        index += PRIME;
+        int currentSize = l.size();
+        if (currentSize > 0) {
+          index %= l.size();
+        }
+      }
+    }
+    return index;
+  }
+}
diff --git a/user/src/com/google/gwt/junit/benchmarks/BenchmarkReport.java b/user/src/com/google/gwt/junit/benchmarks/BenchmarkReport.java
index 2fe858d..79330b3 100644
--- a/user/src/com/google/gwt/junit/benchmarks/BenchmarkReport.java
+++ b/user/src/com/google/gwt/junit/benchmarks/BenchmarkReport.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * 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
@@ -20,9 +20,11 @@
 import com.google.gwt.core.ext.typeinfo.JClassType;
 import com.google.gwt.core.ext.typeinfo.JMethod;
 import com.google.gwt.core.ext.typeinfo.TypeOracle;
+import com.google.gwt.core.ext.typeinfo.HasAnnotations;
 import com.google.gwt.dev.util.Util;
 import com.google.gwt.junit.client.TestResults;
 import com.google.gwt.junit.client.Trial;
+import com.google.gwt.junit.client.Benchmark;
 import com.google.gwt.junit.rebind.BenchmarkGenerator;
 import com.google.gwt.util.tools.Utility;
 
@@ -312,6 +314,8 @@
     return resultString.equals("") ? null : resultString;
   }
 
+  private TreeLogger deprecationBranch;
+
   private TreeLogger logger;
 
   private Parser parser = new Parser();
@@ -326,6 +330,10 @@
 
   public BenchmarkReport(TreeLogger logger) {
     this.logger = logger;
+    deprecationBranch = logger.branch(TreeLogger.INFO,
+        "Scanning Benchmarks for deprecated annotations; "
+            + "Please see " + Benchmark.class.getName()
+            + " for more information.", null);
   }
 
   /**
@@ -340,8 +348,7 @@
   public void addBenchmark(JClassType benchmarkClass, TypeOracle typeOracle) {
 
     this.typeOracle = typeOracle;
-    String categoryType = getSimpleMetaData(benchmarkClass,
-        GWT_BENCHMARK_CATEGORY);
+    String categoryType = getBenchmarkCategory(benchmarkClass);
 
     Map<String, JMethod> zeroArgMethods = BenchmarkGenerator.getNotOverloadedTestMethods(benchmarkClass);
     Map<String, JMethod> parameterizedMethods = BenchmarkGenerator.getParameterizedTestMethods(
@@ -360,8 +367,7 @@
     // Add all of the benchmark methods
     for (JMethod method : testMethods) {
       String methodName = method.getName();
-      String methodCategoryType = getSimpleMetaData(method,
-          GWT_BENCHMARK_CATEGORY);
+      String methodCategoryType = getBenchmarkCategory(method);
       if (methodCategoryType == null) {
         methodCategoryType = categoryType;
       }
@@ -438,6 +444,16 @@
     // docOut.close();
   }
 
+  private <T extends HasMetaData & HasAnnotations> String getBenchmarkCategory(
+      T element) {
+    String category = getSimpleMetaData(element, GWT_BENCHMARK_CATEGORY);
+    if (category != null) {
+      deprecationBranch.log(TreeLogger.WARN, GWT_BENCHMARK_CATEGORY + " has " 
+          + "been deprecated with no replacement.", null);
+    }
+    return category;
+  }
+
   private CategoryImpl getCategory(String name) {
     CategoryImpl c = testCategories.get(name);
 
@@ -445,24 +461,39 @@
       return c;
     }
 
-    String categoryName = "";
-    String categoryDescription = "";
-
-    if (name != null) {
-      JClassType categoryType = typeOracle.findType(name);
-
-      if (categoryType != null) {
-        categoryName = getSimpleMetaData(categoryType, GWT_BENCHMARK_NAME);
-        categoryDescription = getSimpleMetaData(categoryType,
-            GWT_BENCHMARK_DESCRIPTION);
-      }
-    }
-
-    c = new CategoryImpl(name, categoryName, categoryDescription);
+    c = getCategoryMetaData(name); 
     testCategories.put(name, c);
     return c;
   }
 
+  private CategoryImpl getCategoryMetaData(String typeName) {
+    if (typeName == null) {
+      return new CategoryImpl(null, "", "");
+    }
+
+    JClassType categoryType = typeOracle.findType(typeName);
+
+    if (categoryType == null) {
+      return new CategoryImpl(typeName, "", "");
+    }
+
+    String categoryName = getSimpleMetaData(categoryType, GWT_BENCHMARK_NAME);
+    String categoryDescription = getSimpleMetaData(categoryType,
+        GWT_BENCHMARK_DESCRIPTION);
+
+    if (categoryName != null || categoryDescription != null) {
+      deprecationBranch.log(TreeLogger.WARN, GWT_BENCHMARK_NAME + " and " + 
+          GWT_BENCHMARK_DESCRIPTION + " have been deprecated with no " 
+          + "replacement", null);
+    }
+
+    categoryName = categoryName == null ? "" : categoryName;
+    categoryDescription = categoryDescription == null ? "" : categoryDescription;
+    
+    return new CategoryImpl(typeName, categoryName,
+        categoryDescription);
+  }
+
   /**
    * Parses out the JavaDoc comment from a string of source code. Returns the
    * first sentence summary in <code>summary</code> and the body of the entire
diff --git a/user/src/com/google/gwt/junit/client/Benchmark.java b/user/src/com/google/gwt/junit/client/Benchmark.java
index eaa2a65..e226308 100644
--- a/user/src/com/google/gwt/junit/client/Benchmark.java
+++ b/user/src/com/google/gwt/junit/client/Benchmark.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * 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
@@ -17,71 +17,56 @@
 
 /**
  * A type of {@link com.google.gwt.junit.client.GWTTestCase} which specifically
- * records performance results. {@link com.google.gwt.junit.client.Benchmark}s
- * have additional functionality above and beyond GWT's JUnit support for
- * standard <code>TestCases</code>.
+ * records performance results. {@code Benchmarks} have additional functionality
+ * above and beyond GWT's JUnit support for standard <code>TestCases</code>.
  *
- * <ul>
- * <li>In a single <code>JUnit</code> run, the results of all executed
+ * <h2>Reporting</h2>
+ * <p>
+ * In a single <code>JUnit</code> run, the results of all executed
  * benchmarks are collected and stored in an XML report viewable with the
- * <code>benchmarkViewer</code>.</li>
+ * <code>benchmarkViewer</code>.
+ * </p><br>
  *
- * <li>GWT automatically removes jitter from your benchmark methods by running
- * them for a minimum period of time (150ms). GWT also optionally limits your
- * benchmark execution to a maximum period of time (1000ms).</li>
+ * <h2>Permutations</h2>
+ * <p>GWT supports test methods that have parameters. GWT will execute each
+ * benchmark method multiple times in order to exhaustively test all the 
+ * possible combinations of parameter values. All of your test method parameters 
+ * must be annotated with a {@code Range} annotation such as 
+ * {@link com.google.gwt.junit.client.annotations.RangeField RangeField}
+ * or {@link com.google.gwt.junit.client.annotations.RangeEnum RangeEnum}.
  *
- * <li>GWT supports "begin" and "end" test methods that separate setup and
- * teardown costs from the actual work being benchmarked. Simply name your
- * functions "begin[TestMethodName]" and "end[TestMethodName]" and they will
- * be executed before and after every execution of your test method. The
- * timings of these setup methods are not included in the test results.</li>
+ * For example,
  *
- * <li>GWT supports test methods that have parameters. GWT will execute each
- * benchmark method multiple times in order to exhaustively test all the possible
- * combinations of parameter values. For each parameter that your test method
- * accepts, it should document it with the annotation,
- * <code>&#64;gwt.benchmark.param</code>.
- *
- * <p>The syntax for gwt.benchmark.param is
- * <code>&lt;param name&gt; = &lt;Iterable&gt;</code>. For example,
- *
- * <pre>
- * &#64;gwt.benchmark.param where = java.util.Arrays.asList(
- *   new Position[] { Position.BEGIN, Position.END, Position.VARIED } )
- * &#64;gwt.benchmark.param size -limit = insertRemoveRange
- * public void testArrayListRemoves(Position where, Integer size) { ... }
- * </pre></p>
- *
- * <p>In this example, the annotated function is executed with all the possible
- * permutations of <code>Position = (BEGIN, END, and VARIED)</code> and
- * <code>insertRemoveRange = IntRange( 64, Integer.MAX_VALUE, "*", 2 )</code>.
+ * <code><pre> 
+ * public void testArrayListRemoves(
+ *   &#64;RangeEnum(Position.class) Position where, 
+ *   &#64;RangeField("insertRemoveRange") Integer size) { ... 
+ * }
+ * </pre></code>
  * </p>
+ * 
+ * <h2>Timing</h2>
+ * <ul>
+ * <li>GWT automatically removes jitter from your benchmark methods by running
+ * them for a minimum period of time (150ms).</li>
  *
- * <p>This particular example also demonstrates how GWT can automatically limit
- * the number of executions of your test. Your final parameter (in this example,
- * size) can optionally be decorated with -limit to indicate to GWT that
- * it should stop executing additional permutations of the test when the
- * execution time becomes too long (over 1000ms). So, in this example,
- * for each value of <code>Position</code>, <code>testArrayListRemoves</code>
- * will be executed for increasing values of <code>size</code> (beginning with
- * 64 and increasing in steps of 2), until either it reaches
- * <code>Integer.MAX_VALUE</code> or the execution time for the last
- * permutation is > 1000ms.</p>
+ * <li>GWT supports {@link com.google.gwt.junit.client.annotations.IterationTimeLimit
+ * time limits} on the maximum duration of each permutation of a benchmark 
+ * method. With this feature, you can supply very high upper bounds on your 
+ * ranges (such as Integer.MAX_VALUE), which future-proofs your benchmarks
+ * against faster hardware.
+ * </li>
+ *
+ * <li>GWT supports {@link com.google.gwt.junit.client.annotations.Setup Setup} 
+ * and {@link com.google.gwt.junit.client.annotations.Teardown Teardown} methods
+ * which separate test overhead from the actual work being benchmarked. The 
+ * timings of these lifecycle methods are excluded from test results.
  * </li>
  * </ul>
  *
- * <p>{@link Benchmark}s support the following annotations on each test method
- * in order to decorate each test with additional information useful for
- * reporting.</p>
- *
- * <ul>
- * <li><code>&#64;gwt.benchmark.category</code> - The class name of the {@link Category} the
- * benchmark belongs to. This property may also be set at the
- * {@link com.google.gwt.junit.client.Benchmark} class level.</li>
- * </ul>
- *
- * <p>Please note that {@link Benchmark}s do not currently support asynchronous
- * testing mode. Calling
+ * <h2>Notes</h2>
+ * <p>Please note that {@code Benchmarks} do not currently support 
+ * asynchronous testing mode. Calling
  * {@link com.google.gwt.junit.client.GWTTestCase#delayTestFinish(int)}
  * or {@link com.google.gwt.junit.client.GWTTestCase#finishTest()} will result
  * in an UnsupportedOperationException.</p>
@@ -96,12 +81,12 @@
  * {@example com.google.gwt.examples.benchmarks.AllocBenchmark}
  *
  * <h3>An advanced benchmark example</h3>
- * {@link com.google.gwt.examples.benchmarks.ArrayListAndVectorBenchmark} is a more
- * sophisticated example of benchmarking. It demonstrates the use of "begin"
- * and "end" test methods, parameterized test methods, and automatic
- * test execution limits.
+ * {@link com.google.gwt.examples.benchmarks.ArrayListBenchmark} is a 
+ * more sophisticated example of benchmarking. It demonstrates the use of 
+ * {@code Setup} and {@code Teardown} test methods, parameterized test methods,
+ *  and time limits.
  *
- * {@example com.google.gwt.examples.benchmarks.ArrayListAndVectorBenchmark}
+ * {@example com.google.gwt.examples.benchmarks.ArrayListBenchmark}
  */
 public abstract class Benchmark extends GWTTestCase {
 
diff --git a/user/src/com/google/gwt/junit/client/Category.java b/user/src/com/google/gwt/junit/client/Category.java
index 6ef1147..ff9c233 100644
--- a/user/src/com/google/gwt/junit/client/Category.java
+++ b/user/src/com/google/gwt/junit/client/Category.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * 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
@@ -16,20 +16,10 @@
 package com.google.gwt.junit.client;
 
 /**
- * A benchmark category. {@link com.google.gwt.junit.client.Benchmark}s which
- * use the GWT annotation, <code>@gwt.benchmark.category</code>, must set it to
- * a class which implements this interface.
- *
- * <p>The following GWT annotations can be set on a <code>Category</code>:
- *
- * <ul>
- *   <li><code>@gwt.benchmark.name</code> The name of the <code>Category</code>
- * </li>
- *  <li><code>@gwt.benchmark.description</code> The description of the
- * <code>Category</code></li>
- * </ul>
- * </p>
+ * A named category that provides classification for
+ * {@link Benchmark Benchmarks}. Categories are now deprecated.
  * 
  */
+@Deprecated
 public interface Category {
 }
diff --git a/user/src/com/google/gwt/junit/client/Range.java b/user/src/com/google/gwt/junit/client/Range.java
index 4ee369c..db00c8a 100644
--- a/user/src/com/google/gwt/junit/client/Range.java
+++ b/user/src/com/google/gwt/junit/client/Range.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * 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
@@ -26,7 +26,6 @@
  *
  * @param <T> the type that this range contains
  * 
- * TODO: Should iterator() return a RangeIterator&lt;T&gt;?
  */
 public interface Range<T> extends Iterable<T> {
 }
diff --git a/user/src/com/google/gwt/junit/client/annotations/IterationTimeLimit.java b/user/src/com/google/gwt/junit/client/annotations/IterationTimeLimit.java
new file mode 100644
index 0000000..bf578d5
--- /dev/null
+++ b/user/src/com/google/gwt/junit/client/annotations/IterationTimeLimit.java
@@ -0,0 +1,41 @@
+/*
+ * 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.junit.client.annotations;
+
+import java.lang.annotation.Target;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Documented;
+
+/**
+ * Specifies a custom time limit for iterations on the decorated
+ * {@link com.google.gwt.junit.client.Benchmark Benchmark} method. Methods 
+ * that aren't explicitly decorated with an IterationTimeLimit, receive the 
+ * default value.
+ * 
+ */
+@Target(ElementType.METHOD)
+@Documented
+public @interface IterationTimeLimit {
+
+  /**
+   * The maximum amount of time, in milliseconds, an iteration is persued before
+   * skipping to the next set of values in the {@code Range}. A value of 0 means
+   * that all values in the {@code Range} will be exhaustively tested.
+   * 
+   * @return a maximum duration in milliseconds >= 0
+   */
+  long value() default 1000;
+}
diff --git a/user/src/com/google/gwt/junit/client/annotations/RangeEnum.java b/user/src/com/google/gwt/junit/client/annotations/RangeEnum.java
new file mode 100644
index 0000000..056710c
--- /dev/null
+++ b/user/src/com/google/gwt/junit/client/annotations/RangeEnum.java
@@ -0,0 +1,38 @@
+/*
+ * 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.junit.client.annotations;
+
+import java.lang.annotation.Target;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Documented;
+
+/**
+ * Specifies an Enum containing the entire range of values for a parameter
+ * to a {@link com.google.gwt.junit.client.Benchmark} method.
+ * 
+ */
+@Target(ElementType.PARAMETER)
+@Documented
+public @interface RangeEnum {
+
+  /**
+   * An <code>Enum</code> that contains the range of values that will be
+   * supplied to the test method.
+   * 
+   * @return For example, {@code MyEnum.class}
+   */
+  Class<? extends Enum<?>> value();
+}
diff --git a/user/src/com/google/gwt/junit/client/annotations/RangeField.java b/user/src/com/google/gwt/junit/client/annotations/RangeField.java
new file mode 100644
index 0000000..ca569c2
--- /dev/null
+++ b/user/src/com/google/gwt/junit/client/annotations/RangeField.java
@@ -0,0 +1,42 @@
+/*
+ * 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.junit.client.annotations;
+
+import java.lang.annotation.Target;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Documented;
+
+/**
+ * Specifies a field containing the entire range of values for a parameter
+ * to a {@link com.google.gwt.junit.client.Benchmark} method. The field must belong
+ * to the same class being decorated by this annotation. The field must
+ * be either an Iterable, Enum, or array whose type matches the parameter
+ * being annotated.
+ * 
+ * Also see {@link RangeEnum}.
+ * 
+ */
+@Target(ElementType.PARAMETER)
+@Documented
+public @interface RangeField {
+
+  /**
+   * The name of the field that this range refers to.
+   * 
+   * @return For example, {@code myCommonRange}.
+   */
+  String value();
+}
diff --git a/user/src/com/google/gwt/junit/client/annotations/Setup.java b/user/src/com/google/gwt/junit/client/annotations/Setup.java
new file mode 100644
index 0000000..6bd3b6a
--- /dev/null
+++ b/user/src/com/google/gwt/junit/client/annotations/Setup.java
@@ -0,0 +1,44 @@
+/*
+ * 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.junit.client.annotations;
+
+import java.lang.annotation.Target;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Documented;
+
+/**
+ * Specifies a setup method that will be executed before the annotated
+ * {@link com.google.gwt.junit.client.Benchmark} test method. Setup methods are automatically executed
+ * by the benchmarking framework before their matching test methods. Setup
+ * measurements are excluded from final benchmark reports.
+ * 
+ * <p>For example, you might annotate a {@code Benchmark} method named
+ * <code>testInserts</code> with <code>@Setup("setupTestInserts")</code> to
+ * ensure <code>setupTestInserts</code> is always executed before
+ * <code>testInserts</code>.
+ * 
+ */
+@Target(ElementType.METHOD)
+@Documented
+public @interface Setup {
+
+  /**
+   * The name of the method to execute before the annotated test method.
+   * 
+   * @return For example, "setupTestInserts".
+   */
+  public String value();
+}
diff --git a/user/src/com/google/gwt/junit/client/annotations/Teardown.java b/user/src/com/google/gwt/junit/client/annotations/Teardown.java
new file mode 100644
index 0000000..581a1c4
--- /dev/null
+++ b/user/src/com/google/gwt/junit/client/annotations/Teardown.java
@@ -0,0 +1,44 @@
+/*
+ * 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.junit.client.annotations;
+
+import java.lang.annotation.Target;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Documented;
+
+/**
+ * Specifies a teardown method that will be executed after the annotated
+ * {@link com.google.gwt.junit.client.Benchmark} test method. Teardown methods are automatically executed
+ * by the benchmarking framework after their matching test methods. Teardown
+ * measurements are excluded from final benchmark reports.
+ * 
+ * <p>For example, you might annotate a {@code Benchmark} method named
+ * <code>testInserts</code> with <code>@Teardown("endTestInserts")</code> to
+ * ensure <code>endTestInserts</code> is always executed after
+ * <code>testInserts</code>.
+ * 
+ */
+@Target(ElementType.METHOD)
+@Documented
+public @interface Teardown {
+
+  /**
+   * The name of the method to execute after the annotated test method.
+   * 
+   * @return For example, "endTestInserts".
+   */
+  public String value();
+}
diff --git a/user/src/com/google/gwt/junit/client/impl/IterableAdapter.java b/user/src/com/google/gwt/junit/client/impl/IterableAdapter.java
new file mode 100644
index 0000000..140c6a5
--- /dev/null
+++ b/user/src/com/google/gwt/junit/client/impl/IterableAdapter.java
@@ -0,0 +1,46 @@
+/*
+ * 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.junit.client.impl;
+
+import java.util.Arrays;
+
+/**
+ * Provides convenience methods for adapting various values to the Iterable
+ * interface.
+ * 
+ */
+public class IterableAdapter {
+
+  /**
+   * Returns an <code>Iterable</code> from an array.
+   * 
+   * @param array a not <code>null</code> array
+   * @return an <code>Iterable</code> that wraps the array
+   */
+  public static <T> Iterable<T> toIterable(T[] array) {
+    return Arrays.asList(array);
+  }
+
+  /**
+   * Returns <code>iterable</code> as itself. Useful for code-gen situations.
+   * 
+   * @param iterable a maybe <code>null</code> <code>Iterable</code>
+   * @return <code>iterable</code>
+   */
+  public static <T> Iterable<T> toIterable(Iterable<T> iterable) {
+    return iterable;
+  }
+}
diff --git a/user/src/com/google/gwt/junit/client/impl/PermutationIterator.java b/user/src/com/google/gwt/junit/client/impl/PermutationIterator.java
index b660871..25d1504 100644
--- a/user/src/com/google/gwt/junit/client/impl/PermutationIterator.java
+++ b/user/src/com/google/gwt/junit/client/impl/PermutationIterator.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * 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
@@ -15,32 +15,29 @@
  */
 package com.google.gwt.junit.client.impl;
 
-import com.google.gwt.junit.client.Range;
-
 import java.util.Iterator;
 import java.util.List;
 import java.util.ArrayList;
 import java.util.Arrays;
 
 /**
- * Iterates over all the possible permutations available in a list of
- * {@link com.google.gwt.junit.client.Range}s.
- * 
- * <p>
- * The simplest way to iterate over the permutations of multiple iterators is in
- * a nested for loop. The PermutationIterator turns that for loop inside out
- * into a single iterator, which enables you to access each permutation in a
- * piecemeal fashion.
- * </p>
+ * Iterates over all the possible permutations available in a list of {@link
+ * Iterable Iterables}.
+ *
+ * <p> The simplest way to iterate over the permutations of multiple iterators
+ * is in a nested for loop. The PermutationIterator turns that for loop inside
+ * out into a single iterator, which enables you to access each permutation in a
+ * piecemeal fashion. </p>
  */
-public class PermutationIterator implements
-    Iterator<PermutationIterator.Permutation> {
+public class PermutationIterator
+    implements Iterator<PermutationIterator.Permutation> {
 
   /**
    * A single permutation of all the iterators. Contains the current value of
    * each iterator for the permutation.
    */
   public static class Permutation {
+
     private List<Object> values;
 
     public Permutation(List<?> values) {
@@ -57,45 +54,27 @@
     }
   }
 
-  /**
-   * A Range implemented using a list of data.
-   * 
-   * @param <T> the type of data in the range.
-   */
-  private static class ListRange<T> implements Range<T> {
-    private List<T> list;
-
-    public ListRange(List<T> list) {
-      this.list = list;
-    }
-
-    public Iterator<T> iterator() {
-      return list.iterator();
-    }
-  }
-
   public static void main(String[] args) {
-    List<Range<String>> ranges = new ArrayList<Range<String>>(3);
-    ranges.add(new ListRange<String>(
-        Arrays.asList("a", "b", "c")));
-    ranges.add(new ListRange<String>(
-        Arrays.asList("1", "2", "3")));
-    ranges.add(new ListRange<String>(Arrays.asList(
-        "alpha", "beta", "gamma", "delta")));
+    List<Iterable<String>> iterables = new ArrayList<Iterable<String>>(3);
+    iterables.add(Arrays.asList("a", "b", "c"));
+    iterables.add(Arrays.asList("1", "2", "3"));
+    iterables.add(Arrays.asList("alpha", "beta", "gamma", "delta"));
 
     System.out.println("Testing normal iteration.");
-    for (Iterator<Permutation> it = new PermutationIterator(ranges); it.hasNext();) {
+    for (Iterator<Permutation> it = new PermutationIterator(iterables);
+        it.hasNext();) {
       Permutation p = it.next();
       System.out.println(p);
     }
 
     System.out.println("\nTesting skipping iteration.");
 
-    Iterator<String> skipIterator = Arrays.asList(
-        "alpha", "beta", "gamma", "delta").iterator();
+    Iterator<String> skipIterator = Arrays
+        .asList("alpha", "beta", "gamma", "delta").iterator();
     boolean skipped = true;
     String skipValue = null;
-    for (PermutationIterator it = new PermutationIterator(ranges); it.hasNext();) {
+    for (PermutationIterator it = new PermutationIterator(iterables);
+        it.hasNext();) {
       Permutation p = it.next();
 
       if (skipped) {
@@ -132,9 +111,9 @@
   private boolean maybeHaveMore = true;
 
   /**
-   * The ranges to permutate.
+   * The {@code Iterables} to permute.
    */
-  private List<? extends Range<?>> ranges;
+  private List<? extends Iterable<?>> iterables;
 
   /**
    * Did we just skip a range? If so, the values List already contains the
@@ -149,22 +128,21 @@
 
   /**
    * Constructs a new PermutationIterator that provides the values for each
-   * possible permutation of <code>ranges</code>.
-   * 
-   * @param ranges non-null. Each {@link com.google.gwt.junit.client.Range} must
-   * have at least one element. ranges.size() must be > 1
-   * 
-   * TODO(tobyr) Consider if empty Ranges ever make sense in the context of
-   * permutations.
-   * 
+   * possible permutation of <code>iterables</code>.
+   *
+   * @param iterables non-null. Each {@link Iterable} must have at least one
+   * element. iterables.size() must be > 1
+   *
+   * TODO(tobyr) Consider if empty Iterables ever make sense in
+   * the context of permutations.
    */
-  public PermutationIterator(List<? extends Range<?>> ranges) {
-    this.ranges = ranges;
+  public PermutationIterator(List<? extends Iterable<?>> iterables) {
+    this.iterables = iterables;
 
     iterators = new ArrayList<Iterator<?>>();
 
-    for (Range<?> r : ranges) {
-      iterators.add(r.iterator());
+    for (Iterable<?> iterable : iterables) {
+      iterators.add(iterable.iterator());
     }
 
     values = new ArrayList<Object>();
@@ -173,7 +151,7 @@
   /**
    * Returns a new <code>Permutation</code> containing the values of the next
    * permutation.
-   * 
+   *
    * @return a non-null <code>Permutation</code>
    */
   public boolean hasNext() {
@@ -185,7 +163,8 @@
     // Walk the iterators from bottom to top checking to see if any still have
     // any available values
 
-    for (int currentIterator = iterators.size() - 1; currentIterator >= 0; --currentIterator) {
+    for (int currentIterator = iterators.size() - 1; currentIterator >= 0;
+        --currentIterator) {
       Iterator<?> it = iterators.get(currentIterator);
       if (it.hasNext()) {
         return true;
@@ -216,13 +195,14 @@
     // Walk through the iterators from bottom to top, finding the first one
     // which has a value available. Increment it, reset all of the subsequent
     // iterators, and then return the current permutation.
-    for (int currentIteratorIndex = iterators.size() - 1; currentIteratorIndex >= 0; --currentIteratorIndex) {
+    for (int currentIteratorIndex = iterators.size() - 1;
+        currentIteratorIndex >= 0; --currentIteratorIndex) {
       Iterator<?> it = iterators.get(currentIteratorIndex);
       if (it.hasNext()) {
         values.set(currentIteratorIndex, it.next());
         for (int i = currentIteratorIndex + 1; i < iterators.size(); ++i) {
-          Range<?> resetRange = ranges.get(i);
-          Iterator<?> resetIterator = resetRange.iterator();
+          Iterable<?> resetIterable = iterables.get(i);
+          Iterator<?> resetIterator = resetIterable.iterator();
           iterators.set(i, resetIterator);
           values.set(i, resetIterator.next());
         }
@@ -240,24 +220,23 @@
   }
 
   /**
-   * Skips the remaining set of values in the bottom
-   * {@link com.google.gwt.junit.client.Range}. This method affects the results
-   * of both hasNext() and next().
-   * 
+   * Skips the remaining set of values in the bottom {@code Iterable}. This
+   * method affects the results of both hasNext() and next().
    */
   public void skipCurrentRange() {
 
     rangeSkipped = true;
 
-    for ( int currentIteratorIndex = iterators.size() - 2; currentIteratorIndex >= 0; --currentIteratorIndex ) {
-      Iterator<?> it = iterators.get( currentIteratorIndex );
-      if ( it.hasNext() ) {
-        values.set( currentIteratorIndex, it.next() );
-        for ( int i = currentIteratorIndex + 1; i < iterators.size(); ++i ) {
-          Range<?> resetRange = ranges.get( i );
-          Iterator<?> resetIterator = resetRange.iterator();
-          iterators.set( i, resetIterator );
-          values.set( i, resetIterator.next() );
+    for (int currentIteratorIndex = iterators.size() - 2;
+        currentIteratorIndex >= 0; --currentIteratorIndex) {
+      Iterator<?> it = iterators.get(currentIteratorIndex);
+      if (it.hasNext()) {
+        values.set(currentIteratorIndex, it.next());
+        for (int i = currentIteratorIndex + 1; i < iterators.size(); ++i) {
+          Iterable<?> resetIterable = iterables.get(i);
+          Iterator<?> resetIterator = resetIterable.iterator();
+          iterators.set(i, resetIterator);
+          values.set(i, resetIterator.next());
         }
         return;
       }
diff --git a/user/src/com/google/gwt/junit/rebind/BenchmarkGenerator.java b/user/src/com/google/gwt/junit/rebind/BenchmarkGenerator.java
index df74865..5ddbfeb 100644
--- a/user/src/com/google/gwt/junit/rebind/BenchmarkGenerator.java
+++ b/user/src/com/google/gwt/junit/rebind/BenchmarkGenerator.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * 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
@@ -20,7 +20,14 @@
 import com.google.gwt.core.ext.typeinfo.JClassType;
 import com.google.gwt.core.ext.typeinfo.JMethod;
 import com.google.gwt.core.ext.typeinfo.JParameter;
+import com.google.gwt.core.ext.typeinfo.JField;
 import com.google.gwt.junit.JUnitShell;
+import com.google.gwt.junit.client.annotations.IterationTimeLimit;
+import com.google.gwt.junit.client.annotations.Setup;
+import com.google.gwt.junit.client.annotations.Teardown;
+import com.google.gwt.junit.client.annotations.RangeField;
+import com.google.gwt.junit.client.annotations.RangeEnum;
+import com.google.gwt.junit.client.Benchmark;
 import com.google.gwt.dev.generator.ast.ForLoop;
 import com.google.gwt.dev.generator.ast.MethodCall;
 import com.google.gwt.dev.generator.ast.Statement;
@@ -34,6 +41,7 @@
 import java.util.HashMap;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.lang.reflect.Method;
 
 /**
  * Implements a generator for Benchmark classes. Benchmarks require additional
@@ -41,8 +49,9 @@
  */
 public class BenchmarkGenerator extends JUnitTestCaseStubGenerator {
 
-  private static class MutableBoolean {
-    boolean value;
+  private static class MutableLong {
+
+    long value;
   }
 
   private static final String BEGIN_PREFIX = "begin";
@@ -55,20 +64,23 @@
 
   private static final String ESCAPE_LOOP = "__escapeLoop";
 
+  private static long defaultTimeout = -1;
+
   /**
    * Returns all the zero-argument JUnit test methods that do not have
    * overloads.
    *
    * @return Map<String,JMethod>
    */
-  public static Map<String, JMethod> getNotOverloadedTestMethods(JClassType requestedClass) {
-    Map<String, List<JMethod>> methods =
-      getAllMethods(requestedClass, new MethodFilter() {
-      public boolean accept(JMethod method) {
-        return isJUnitTestMethod(method, true);
-      }
-    });
-    
+  public static Map<String, JMethod> getNotOverloadedTestMethods(
+      JClassType requestedClass) {
+    Map<String, List<JMethod>> methods = getAllMethods(requestedClass,
+        new MethodFilter() {
+          public boolean accept(JMethod method) {
+            return isJUnitTestMethod(method, true);
+          }
+        });
+
     // Create a new map to store the methods
     Map<String, JMethod> notOverloadedMethods = new HashMap<String, JMethod>();
     for (Map.Entry<String, List<JMethod>> entry : methods.entrySet()) {
@@ -87,19 +99,18 @@
    *
    * @return Map<String,JMethod>
    */
-  public static Map<String, JMethod> getParameterizedTestMethods(JClassType requestedClass,
-      TreeLogger logger) {
-
-    Map<String, List<JMethod>> testMethods =
-      getAllMethods(requestedClass, new MethodFilter() {
-      public boolean accept(JMethod method) {
-        return isJUnitTestMethod(method, true);
-      }
-    });
+  public static Map<String, JMethod> getParameterizedTestMethods(
+      JClassType requestedClass, TreeLogger logger) {
+    Map<String, List<JMethod>> testMethods = getAllMethods(requestedClass,
+        new MethodFilter() {
+          public boolean accept(JMethod method) {
+            return isJUnitTestMethod(method, true);
+          }
+        });
 
     // Create a new mapping to return
     Map<String, JMethod> overloadedMethods = new HashMap<String, JMethod>();
-    
+
     // Remove all non-overloaded test methods
     for (Map.Entry<String, List<JMethod>> entry : testMethods.entrySet()) {
       String name = entry.getKey();
@@ -107,8 +118,8 @@
 
       if (methods.size() > 2) {
         String msg = requestedClass + "." + name
-            + " has more than one overloaded version.\n" +
-            "It will not be included in the test case execution.";
+            + " has more than one overloaded version.\n"
+            + "It will not be included in the test case execution.";
         logger.log(TreeLogger.WARN, msg, null);
         continue;
       }
@@ -122,8 +133,8 @@
            * test to make the benchmarks run correctly (JUnit artifact).
            */
           String msg = requestedClass + "." + name
-              + " does not have a zero-argument overload.\n" +
-              "It will not be included in the test case execution.";
+              + " does not have a zero-argument overload.\n"
+              + "It will not be included in the test case execution.";
           logger.log(TreeLogger.WARN, msg, null);
         }
         // Only a zero-argument version, we don't need to process it.
@@ -149,8 +160,8 @@
 
       if (noArgMethod == null) {
         String msg = requestedClass + "." + name
-            + " does not have a zero-argument overload.\n" +
-            "It will not be included in the test case execution.";
+            + " does not have a zero-argument overload.\n"
+            + "It will not be included in the test case execution.";
         logger.log(TreeLogger.WARN, msg, null);
         continue;
       }
@@ -161,16 +172,28 @@
     return overloadedMethods;
   }
 
-  private static JMethod getBeginMethod(JClassType type, String name) {
-    StringBuffer methodName = new StringBuffer(name);
-    methodName.replace(0, "test".length(), BEGIN_PREFIX);
-    return getMethod(type, methodName.toString());
+  private static JMethod getBeginMethod(JClassType type, JMethod method) {
+    Setup setup = method.getAnnotation(Setup.class);
+    String methodName;
+    if (setup != null) {
+      methodName = setup.value();
+    } else {
+      methodName = new StringBuffer(method.getName())
+          .replace(0, "test".length(), BEGIN_PREFIX).toString();
+    }
+    return getMethod(type, methodName);
   }
 
-  private static JMethod getEndMethod(JClassType type, String name) {
-    StringBuffer methodName = new StringBuffer(name);
-    methodName.replace(0, "test".length(), END_PREFIX);
-    return getMethod(type, methodName.toString());
+  private static JMethod getEndMethod(JClassType type, JMethod method) {
+    Teardown teardown = method.getAnnotation(Teardown.class);
+    String methodName;
+    if (teardown != null) {
+      methodName = teardown.value();
+    } else {
+      methodName = new StringBuffer(method.getName())
+          .replace(0, "test".length(), END_PREFIX).toString();
+    }
+    return getMethod(type, methodName);
   }
 
   private static JMethod getMethod(JClassType type, MethodFilter filter) {
@@ -191,10 +214,16 @@
     });
   }
 
+  private TreeLogger deprecationBranch;
+
   @Override
   public void writeSource() throws UnableToCompleteException {
     super.writeSource();
 
+    deprecationBranch = logger.branch(TreeLogger.TRACE,
+        "Scanning Benchmarks for deprecated annotations; " + "Please see "
+            + Benchmark.class.getName() + " for more information.", null);
+
     generateEmptyFunc(getSourceWriter());
     implementZeroArgTestMethods();
     implementParameterizedTestMethods();
@@ -211,7 +240,8 @@
    *         wrapped <code>stmts</code>
    */
   private Statements benchmark(Statements stmts, String timeMillisName,
-      boolean generateEscape, Statements recordCode, Statements breakCode) {
+      long bound, Statements recordCode, Statements breakCode)
+      throws UnableToCompleteException {
     Statements benchmarkCode = new StatementsList();
     List<Statements> benchStatements = benchmarkCode.getStatements();
 
@@ -225,17 +255,15 @@
     loopStatements.add(runLoop);
 
     // Put the rest of the code in 1 big statement to simplify things
-    String benchCode =
-        "long duration = System.currentTimeMillis() - start;\n\n" +
+    String benchCode = "long duration = System.currentTimeMillis() - start;\n\n"
+        +
 
-        "if ( duration < 150 ) {\n" +
-        "  numLoops += numLoops;\n" +
-        "  continue;\n" +
-        "}\n\n" +
+        "if ( duration < 150 ) {\n" + "  numLoops += numLoops;\n"
+        + "  continue;\n" + "}\n\n" +
 
-        "double durationMillis = duration * 1.0;\n" +
-        "double numLoopsAsDouble = numLoops * 1.0;\n" +
-        timeMillisName + " = durationMillis / numLoopsAsDouble";
+        "double durationMillis = duration * 1.0;\n"
+        + "double numLoopsAsDouble = numLoops * 1.0;\n" + timeMillisName
+        + " = durationMillis / numLoopsAsDouble";
 
     loopStatements.add(new Statement(benchCode));
 
@@ -243,12 +271,10 @@
       loopStatements.add(recordCode);
     }
 
-    if (generateEscape) {
-      loopStatements.add(new Statement(
-          "if ( numLoops == 1 && duration > 1000 ) {\n" +
-            breakCode.toString() + "\n" +
-          "}\n\n"
-      ));
+    if (bound != 0) {
+      loopStatements.add(new Statement("if ( numLoops == 1 && duration > "
+          + bound + " ) {\n" + breakCode.toString() + "\n"
+          + "}\n\n"));
     }
 
     loopStatements.add(new Statement("break"));
@@ -256,44 +282,17 @@
     return benchmarkCode;
   }
 
-  /**
-   * Generates code that executes <code>statements</code> for all possible
-   * values of <code>params</code>. Exports a label named ESCAPE_LOOP that
-   * points to the the "inner loop" that should be escaped to for a limited
-   * variable.
-   *
-   * @return the generated code
-   * TODO: Is this used anywhere?
-   */
-  private Statements executeForAllValues(JParameter[] methodParams,
-      Map<String, String> params, Statements statements) {
-    Statements root = new StatementsList();
-    Statements currentContext = root;
-
-    // Profile the setup and teardown costs for this test method
-    // but only if 1 of them exists.
-    for (int i = 0; i < methodParams.length; ++i) {
-      JParameter methodParam = methodParams[i];
-      String paramName = methodParam.getName();
-      String paramValue = params.get(paramName);
-      String typeName = methodParam.getType().getQualifiedSourceName();
-
-      String iteratorName = "it_" + paramName;
-      String initializer = "java.util.Iterator<" + typeName + "> " + iteratorName + " = "
-          + paramValue + ".iterator()";
-      ForLoop loop = new ForLoop(initializer, iteratorName + ".hasNext()", "");
-      if (i == methodParams.length - 1) {
-        loop.setLabel(ESCAPE_LOOP);
+  private boolean fieldExists(JClassType type, String fieldName) {
+    JField field = type.findField(fieldName);
+    if (field == null) {
+      JClassType superClass = type.getSuperclass();
+      // noinspection SimplifiableIfStatement
+      if (superClass == null) {
+        return false;
       }
-      currentContext.getStatements().add(loop);
-      loop.getStatements().add(new Statement(typeName + " " + paramName + " = "
-          + iteratorName + ".next()"));
-      currentContext = loop;
+      return fieldExists(superClass, fieldName);
     }
-
-    currentContext.getStatements().add(statements);
-
-    return root;
+    return true;
   }
 
   private Statements genBenchTarget(JMethod beginMethod, JMethod endMethod,
@@ -317,10 +316,10 @@
   }
 
   /**
-   * Currently, the benchmarking subsystem does not support async Benchmarks,
-   * so we need to generate some additional code that prevents the user
-   * from entering async mode in their Benchmark, even though we're using
-   * it internally.
+   * Currently, the benchmarking subsystem does not support async Benchmarks, so
+   * we need to generate some additional code that prevents the user from
+   * entering async mode in their Benchmark, even though we're using it
+   * internally.
    *
    * Generates the code for the "supportsAsync" functionality in the
    * translatable version of GWTTestCase. This includes:
@@ -334,29 +333,29 @@
   private void generateAsyncCode() {
     SourceWriter writer = getSourceWriter();
 
-    writer.println( "private boolean supportsAsync;" );
+    writer.println("private boolean supportsAsync;");
     writer.println();
-    writer.println( "public boolean supportsAsync() {");
-    writer.println( "  return supportsAsync;");
-    writer.println( "}");
+    writer.println("public boolean supportsAsync() {");
+    writer.println("  return supportsAsync;");
+    writer.println("}");
     writer.println();
-    writer.println( "private void privateDelayTestFinish(int timeout) {" );
-    writer.println( "  supportsAsync = true;");
-    writer.println( "  try {");
-    writer.println( "    delayTestFinish(timeout);");
-    writer.println( "  } finally {");
-    writer.println( "    supportsAsync = false;");
-    writer.println( "  }");
-    writer.println( "}");
+    writer.println("private void privateDelayTestFinish(int timeout) {");
+    writer.println("  supportsAsync = true;");
+    writer.println("  try {");
+    writer.println("    delayTestFinish(timeout);");
+    writer.println("  } finally {");
+    writer.println("    supportsAsync = false;");
+    writer.println("  }");
+    writer.println("}");
     writer.println();
-    writer.println( "private void privateFinishTest() {" );
-    writer.println( "  supportsAsync = true;");
-    writer.println( "  try {");
-    writer.println( "    finishTest();");
-    writer.println( "  } finally {");
-    writer.println( "    supportsAsync = false;");
-    writer.println( "  }");
-    writer.println( "}");
+    writer.println("private void privateFinishTest() {");
+    writer.println("  supportsAsync = true;");
+    writer.println("  try {");
+    writer.println("    finishTest();");
+    writer.println("  } finally {");
+    writer.println("    supportsAsync = false;");
+    writer.println("  }");
+    writer.println("}");
     writer.println();
   }
 
@@ -383,17 +382,99 @@
     writer.println("}-*/;");
     writer.println();
   }
+  
+  private Map<String, String> getAnnotationMetaData(JMethod method,
+      MutableLong bound) throws UnableToCompleteException {
 
-  private Map<String,String> getParamMetaData(JMethod method,
-      MutableBoolean isBounded) throws UnableToCompleteException {
-    Map<String,String> params = new HashMap<String,String>();
+    IterationTimeLimit limit = method.getAnnotation(IterationTimeLimit.class);
+    // noinspection SimplifiableIfStatement
+    if (limit == null) {
+      bound.value = getDefaultTimeout();
+    } else {
+      bound.value = limit.value();
+    }
 
+    Map<String, String> paramMetaData = new HashMap<String, String>();
+
+    JParameter[] params = method.getParameters();
+
+    for (JParameter param : params) {
+      RangeField rangeField = param.getAnnotation(RangeField.class);
+      if (rangeField != null) {
+        String fieldName = rangeField.value();
+        JClassType enclosingType = method.getEnclosingType(); 
+        if (!fieldExists(enclosingType, fieldName)) {
+          logger.log(TreeLogger.ERROR, "The RangeField annotation on " + 
+              enclosingType + " at " + method + " specifies a field, " +
+              fieldName + ", which could not be found. Perhaps it is " +
+              "mis-spelled?", null);
+          throw new UnableToCompleteException();
+        }
+        paramMetaData.put(param.getName(), fieldName);
+        continue;
+      }
+      RangeEnum rangeEnum = param.getAnnotation(RangeEnum.class);
+      if (rangeEnum != null) {
+        Class<? extends Enum<?>> enumClass = rangeEnum.value();
+        // Handle inner classes
+        String className = enumClass.getName().replace('$', '.');
+        paramMetaData.put(param.getName(), className + ".values()");
+        continue;
+      }
+
+      String msg = "The parameter, " + param.getName() + ", on method, "
+          + method.getName() + ", must have it's range specified"
+          + "by a RangeField or RangeEnum annotation.";
+      logger.log(TreeLogger.ERROR, msg, null);
+      throw new UnableToCompleteException();
+    }
+
+    return paramMetaData;
+  }
+
+  private synchronized long getDefaultTimeout()
+      throws UnableToCompleteException {
+    if (defaultTimeout != -1) {
+      return defaultTimeout;
+    }
+    Method m = null;
+    try {
+      m = IterationTimeLimit.class.getDeclaredMethod("value");
+      defaultTimeout = (Long) m.getDefaultValue();
+    } catch (Exception e) {
+      /* Possibly one of:
+       * - NullPointerException (if somehow TimeLimit weren't an annotation
+       * or value() didn't have a default).
+       * - NoSuchMethodException if we somehow spelled value wrong
+       * - TypeNotPresentException if somehow value were some type of Class
+       * that couldn't be loaded instead of long
+       * It really doesn't make any difference, because regardless of what
+       * could possibly have failed, we'll still need to go this route.
+       */
+      logger.log(TreeLogger.ERROR,
+          "Unable to retrieve the default benchmark time limit", e);
+      throw new UnableToCompleteException();
+    }
+
+    return defaultTimeout;
+  }
+
+  private Map<String, String> getParamMetaData(JMethod method,
+      MutableLong bound) throws UnableToCompleteException {
     String[][] allValues = method.getMetaData(BENCHMARK_PARAM_META);
 
-    if (allValues == null) {
-      return params;
+    if (allValues == null || allValues.length == 0) {
+      return getAnnotationMetaData(method, bound);
     }
 
+    deprecationBranch.log(TreeLogger.WARN, "Deprecated use of "
+        + BENCHMARK_PARAM_META + " at " + method.getEnclosingType()
+        + " in " + method
+        + "; Please use the new Benchmark JDK 1.5 annotations in " +
+        "com.google.gwt.junit.client.annotations.", null);
+
+    Map<String, String> params = new HashMap<String, String>();
+
     for (int i = 0; i < allValues.length; ++i) {
       String[] values = allValues[i];
       StringBuffer result = new StringBuffer();
@@ -409,15 +490,15 @@
         paramName = nameExprs[0];
         // Make sure this is the last parameter
         JParameter[] parameters = method.getParameters();
-        if (! parameters[parameters.length - 1].getName().equals(paramName)) {
+        if (!parameters[parameters.length - 1].getName().equals(paramName)) {
           JClassType cls = method.getEnclosingType();
-          String msg = "Error at " + cls + "." + method.getName() + "\n" +
-              "Only the last parameter of a method can be marked with the -limit flag.";
+          String msg = "Error at " + cls + "." + method.getName() + "\n"
+              + "Only the last parameter of a method can be marked with the -limit flag.";
           logger.log(TreeLogger.ERROR, msg, null);
           throw new UnableToCompleteException();
         }
 
-        isBounded.value = true;
+        bound.value = getDefaultTimeout();
       }
       String paramValue = lhsAndRhs[1].trim();
       params.put(paramName, paramValue);
@@ -426,10 +507,10 @@
     return params;
   }
 
-  private void implementParameterizedTestMethods() throws
-      UnableToCompleteException {
+  private void implementParameterizedTestMethods()
+      throws UnableToCompleteException {
 
-    Map<String,JMethod> parameterizedMethods = getParameterizedTestMethods(
+    Map<String, JMethod> parameterizedMethods = getParameterizedTestMethods(
         getRequestedClass(), logger);
     SourceWriter sw = getSourceWriter();
     JClassType type = getRequestedClass();
@@ -438,20 +519,20 @@
     //   a) overhead (setup + teardown + loop + function calls) and
     //   b) execution time
     // for all possible parameter values
-    for (Map.Entry<String,JMethod> entry : parameterizedMethods.entrySet() ) {
+    for (Map.Entry<String, JMethod> entry : parameterizedMethods.entrySet()) {
       String name = entry.getKey();
       JMethod method = entry.getValue();
-      JMethod beginMethod = getBeginMethod(type, name);
-      JMethod endMethod = getEndMethod(type, name);
+      JMethod beginMethod = getBeginMethod(type, method);
+      JMethod endMethod = getEndMethod(type, method);
 
       sw.println("public void " + name + "() {");
       sw.indent();
       sw.println("  privateDelayTestFinish( 2000 );");
       sw.println();
 
-      MutableBoolean isBounded = new MutableBoolean();
-      Map<String, String> params = getParamMetaData(method, isBounded);
-      validateParams(method, params);
+      MutableLong bound = new MutableLong();
+      Map<String, String> metaDataByParams = getParamMetaData(method, bound);
+      validateParams(method, metaDataByParams);
 
       JParameter[] methodParams = method.getParameters();
       List<String> paramNames = new ArrayList<String>(methodParams.length);
@@ -459,39 +540,35 @@
         paramNames.add(methodParams[i].getName());
       }
 
-      List<String> paramValues = new ArrayList<String>(methodParams.length);
-      for (int i = 0; i < methodParams.length; ++i) {
-        paramValues.add(params.get(methodParams[i].getName()));
-      }
-
-      sw.print( "final java.util.List<com.google.gwt.junit.client.Range/*<?>*/> ranges = java.util.Arrays.asList( new com.google.gwt.junit.client.Range/*<?>*/[] { " );
+      sw.print(
+          "final java.util.List<Iterable<?>> iterables = java.util.Arrays.asList( new Iterable<?>[] { ");
 
       for (int i = 0; i < paramNames.size(); ++i) {
         String paramName = paramNames.get(i);
-        sw.print( params.get(paramName) );
+        sw.print("com.google.gwt.junit.client.impl.IterableAdapter.toIterable("
+            + metaDataByParams.get(paramName) + ")");
         if (i != paramNames.size() - 1) {
-          sw.print( ",");
+          sw.print(",");
         } else {
-          sw.println( "} );" );
+          sw.println("} );");
         }
-        sw.print( " " );
+        sw.print(" ");
       }
 
       sw.println(
-          "final com.google.gwt.junit.client.impl.PermutationIterator permutationIt = new com.google.gwt.junit.client.impl.PermutationIterator( ranges );\n" +
-          "com.google.gwt.user.client.DeferredCommand.addCommand( new com.google.gwt.user.client.IncrementalCommand() {\n" +
-          "  public boolean execute() {\n" +
-          "    privateDelayTestFinish( 10000 );\n" +
-          "    if ( permutationIt.hasNext() ) {\n" +
-          "      com.google.gwt.junit.client.impl.PermutationIterator.Permutation permutation = permutationIt.next();\n"
-      );
+          "final com.google.gwt.junit.client.impl.PermutationIterator permutationIt = new com.google.gwt.junit.client.impl.PermutationIterator(iterables);\n"
+              + "com.google.gwt.user.client.DeferredCommand.addCommand( new com.google.gwt.user.client.IncrementalCommand() {\n"
+              + "  public boolean execute() {\n"
+              + "    privateDelayTestFinish( 10000 );\n"
+              + "    if ( permutationIt.hasNext() ) {\n"
+              + "      com.google.gwt.junit.client.impl.PermutationIterator.Permutation permutation = permutationIt.next();\n");
 
       for (int i = 0; i < methodParams.length; ++i) {
         JParameter methodParam = methodParams[i];
         String typeName = methodParam.getType().getQualifiedSourceName();
         String paramName = paramNames.get(i);
-        sw.println( "      " + typeName + " " + paramName + " = (" +
-                    typeName + ") permutation.getValues().get(" + i + ");");
+        sw.println("      " + typeName + " " + paramName + " = (" + typeName
+            + ") permutation.getValues().get(" + i + ");");
       }
 
       final String setupTimingName = "__setupTiming";
@@ -506,10 +583,11 @@
           new Statement(new MethodCall(method.getName(), paramNames)));
 
       StringBuffer recordResultsCode = new StringBuffer(
-          "com.google.gwt.junit.client.TestResults results = getTestResults();\n" +
-          "com.google.gwt.junit.client.Trial trial = new com.google.gwt.junit.client.Trial();\n" +
-          "trial.setRunTimeMillis( " + testTimingName + " - " + setupTimingName + " );\n" +
-          "java.util.Map<String, String> variables = trial.getVariables();\n");
+          "com.google.gwt.junit.client.TestResults results = getTestResults();\n"
+              + "com.google.gwt.junit.client.Trial trial = new com.google.gwt.junit.client.Trial();\n"
+              + "trial.setRunTimeMillis( " + testTimingName + " - "
+              + setupTimingName + " );\n"
+              + "java.util.Map<String, String> variables = trial.getVariables();\n");
 
       for (String paramName : paramNames) {
         recordResultsCode.append("variables.put( \"")
@@ -522,15 +600,17 @@
       recordResultsCode.append("results.getTrials().add( trial )");
       Statements recordCode = new Statement(recordResultsCode.toString());
 
-      Statements breakCode = new Statement( "  permutationIt.skipCurrentRange()" );
-      setupBench = benchmark(setupBench, setupTimingName, false, null, breakCode);
-      testBench = benchmark(testBench, testTimingName, isBounded.value, recordCode, breakCode);
+      Statements breakCode = new Statement(
+          "  permutationIt.skipCurrentRange()");
+      setupBench = benchmark(setupBench, setupTimingName, 0, null, breakCode);
+      testBench = benchmark(testBench, testTimingName, bound.value, recordCode,
+          breakCode);
 
       Statements testAndSetup = new StatementsList();
       testAndSetup.getStatements().addAll(setupBench.getStatements());
       testAndSetup.getStatements().addAll(testBench.getStatements());
 
-      sw.println( testAndSetup.toString() );
+      sw.println(testAndSetup.toString());
 
       sw.println(
           "      return true;\n" +
@@ -554,17 +634,17 @@
    * implementParameterizedTestMethods and they should probably be refactored
    * into a single function.
    */
-  private void implementZeroArgTestMethods() {
-    Map<String, JMethod> zeroArgMethods =
-      getNotOverloadedTestMethods(getRequestedClass());
+  private void implementZeroArgTestMethods() throws UnableToCompleteException {
+    Map<String, JMethod> zeroArgMethods = getNotOverloadedTestMethods(
+        getRequestedClass());
     SourceWriter sw = getSourceWriter();
     JClassType type = getRequestedClass();
 
     for (Map.Entry<String, JMethod> entry : zeroArgMethods.entrySet()) {
       String name = entry.getKey();
       JMethod method = entry.getValue();
-      JMethod beginMethod = getBeginMethod(type, name);
-      JMethod endMethod = getEndMethod(type, name);
+      JMethod beginMethod = getBeginMethod(type, method);
+      JMethod endMethod = getEndMethod(type, method);
 
       sw.println("public void " + name + "() {");
       sw.indent();
@@ -586,15 +666,15 @@
           Collections.<String>emptyList(), testStatements);
 
       String recordResultsCode =
-          "com.google.gwt.junit.client.TestResults results = getTestResults();\n"  +
-          "com.google.gwt.junit.client.Trial trial = new com.google.gwt.junit.client.Trial();\n"  +
-          "trial.setRunTimeMillis( " + testTimingName + " - " + setupTimingName + " );\n" +
-          "results.getTrials().add( trial )";
+          "com.google.gwt.junit.client.TestResults results = getTestResults();\n"
+              + "com.google.gwt.junit.client.Trial trial = new com.google.gwt.junit.client.Trial();\n"
+              + "trial.setRunTimeMillis( " + testTimingName + " - "
+              + setupTimingName + " );\n" + "results.getTrials().add( trial )";
 
-      Statements breakCode = new Statement( "  break " + ESCAPE_LOOP );
+      Statements breakCode = new Statement("  break " + ESCAPE_LOOP);
 
-      setupBench = benchmark(setupBench, setupTimingName, false, null, breakCode);
-      testBench = benchmark(testBench, testTimingName, true,
+      setupBench = benchmark(setupBench, setupTimingName, 0, null, breakCode);
+      testBench = benchmark(testBench, testTimingName, getDefaultTimeout(),
           new Statement(recordResultsCode), breakCode);
       ForLoop loop = (ForLoop) testBench.getStatements().get(0);
       loop.setLabel(ESCAPE_LOOP);
@@ -616,8 +696,8 @@
 
       if (paramValue == null) {
         String msg = "Could not find the meta data attribute "
-            + BENCHMARK_PARAM_META +
-            " for the parameter " + paramName + " on method " + method
+            + BENCHMARK_PARAM_META + " for the parameter " + paramName
+            + " on method " + method
             .getName();
         logger.log(TreeLogger.ERROR, msg, null);
         throw new UnableToCompleteException();
diff --git a/user/test/com/google/gwt/emultest/java/util/ArrayListBenchmark.java b/user/test/com/google/gwt/emultest/java/util/ArrayListBenchmark.java
new file mode 100644
index 0000000..9de8a5e
--- /dev/null
+++ b/user/test/com/google/gwt/emultest/java/util/ArrayListBenchmark.java
@@ -0,0 +1,29 @@
+/*
+ * 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.emultest.java.util;
+
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ * A {@link ListBenchmark} for {@link ArrayList ArrayLists}.
+ */
+public class ArrayListBenchmark extends ListBenchmark {
+
+  protected List<String> newList() {
+    return new ArrayList<String>();
+  }
+}
diff --git a/user/test/com/google/gwt/emultest/java/util/HashMapBenchmark.java b/user/test/com/google/gwt/emultest/java/util/HashMapBenchmark.java
new file mode 100644
index 0000000..e1486e1
--- /dev/null
+++ b/user/test/com/google/gwt/emultest/java/util/HashMapBenchmark.java
@@ -0,0 +1,198 @@
+/*
+ * 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.emultest.java.util;
+
+import com.google.gwt.junit.client.Range;
+import com.google.gwt.junit.client.IntRange;
+import com.google.gwt.junit.client.Operator;
+import com.google.gwt.junit.client.Benchmark;
+import com.google.gwt.junit.client.annotations.RangeField;
+import com.google.gwt.junit.client.annotations.Setup;
+
+import java.util.HashMap;
+
+/**
+ * Benchmarks the HashMap implementation.
+ */
+public class HashMapBenchmark extends Benchmark {
+
+  protected Range baseRange = new IntRange(32, Integer.MAX_VALUE,
+      Operator.MULTIPLY, 2);
+
+  protected Range containsRange = new IntRange(10, 200, Operator.ADD, 20);
+
+  private HashMap<Object, Object> map;
+
+  public String getModuleName() {
+    return "com.google.gwt.emultest.EmulSuite";
+  }
+
+  public void testHashMapContainsValueInt() {
+  }
+
+  /**
+   * Checks for <code>size</code> values in a populated HashMap. All items are
+   * Integers, and contain duplicate values.
+   */
+  @Setup("beginHashMapContainsValueInt")
+  public void testHashMapContainsValueInt(
+      @RangeField("containsRange")Integer size) {
+    int num = size.intValue();
+    for (int i = 0; i < num; i++) {
+      Integer intVal = new Integer(i);
+      map.containsValue(intVal);
+    }
+  }
+
+  public void testHashMapContainsValueString() {
+  }
+
+  /**
+   * Checks for <code>size</code> values in a populated HashMap. All items are
+   * Strings, and contain duplicate values.
+   */
+  @Setup("beginHashMapContainsValueString")
+  public void testHashMapContainsValueString(
+      @RangeField("containsRange")Integer size) {
+    int num = size.intValue();
+    for (int i = 0; i < num; i++) {
+      String strVal = Integer.toString(i);
+      map.containsValue(strVal);
+    }
+  }
+
+  public void testHashMapDuplicateIntAdds() {
+  }
+
+  /**
+   * Appends <code>size</code> items to an empty HashMap. All items are
+   * Integers, and contain duplicate values.
+   */
+  @Setup("initMap")
+  public void testHashMapDuplicateIntAdds(
+      @RangeField("baseRange")Integer size) {
+    int num = size.intValue();
+    for (int i = 0; i < num; i++) {
+      Integer intVal = new Integer(i / 10);
+      map.put(intVal, intVal);
+    }
+  }
+
+  public void testHashMapDuplicateStringAdds() {
+  }
+
+  /**
+   * Appends <code>size</code> items to an empty HashMap. All items are Strings,
+   * and contain duplicate values.
+   */
+  @Setup("initMap")
+  public void testHashMapDuplicateStringAdds(
+      @RangeField("baseRange")Integer size) {
+    int num = size.intValue();
+    for (int i = 0; i < num; i++) {
+      String strVal = Integer.toString(i / 10);
+      map.put(strVal, strVal);
+    }
+  }
+
+  public void testHashMapIntAdds() {
+  }
+
+  /**
+   * Appends <code>size</code> items to an empty HashMap. All items are
+   * Integers, and do not contain duplicate values.
+   */
+  @Setup("initMap")
+  public void testHashMapIntAdds(@RangeField("baseRange")Integer size) {
+    int num = size.intValue();
+    for (int i = 0; i < num; i++) {
+      Integer intVal = new Integer(i);
+      map.put(intVal, intVal);
+    }
+  }
+
+  public void testHashMapIntGets() {
+  }
+
+  /**
+   * Checks for <code>size</code> values in a populated HashMap. All items are
+   * Integers, and contain duplicate values.
+   */
+  @Setup("beginHashMapIntGets")
+  public void testHashMapIntGets(@RangeField("baseRange")Integer size) {
+    int num = size.intValue();
+    for (int i = 0; i < num; i++) {
+      Integer intVal = new Integer(i);
+      map.get(intVal);
+    }
+  }
+
+  public void testHashMapStringAdds() {
+  }
+
+  /**
+   * Appends <code>size</code> items to an empty HashMap. All items are Strings,
+   * and do not contain duplicate values.
+   */
+  @Setup("initMap")
+  public void testHashMapStringAdds(@RangeField("baseRange")Integer size) {
+    int num = size.intValue();
+    for (int i = 0; i < num; i++) {
+      String strVal = Integer.toString(i);
+      map.put(strVal, strVal);
+    }
+  }
+
+  public void testHashMapStringGets() {
+  }
+
+  /**
+   * Checks for size values in a populated HashMap. All items are Strings, and
+   * contain duplicate values.
+   */
+  @Setup("beginHashMapStringGets")
+  public void testHashMapStringGets(@RangeField("baseRange")Integer size) {
+    int num = size.intValue();
+    for (int i = 0; i < num; i++) {
+      String strVal = Integer.toString(i);
+      map.get(strVal);
+    }
+  }
+
+  protected void beginHashMapContainsValueInt(Integer size) {
+    map = new HashMap<Object, Object>();
+    testHashMapDuplicateIntAdds(size);
+  }
+
+  protected void beginHashMapContainsValueString(Integer size) {
+    map = new HashMap<Object, Object>();
+    testHashMapDuplicateStringAdds(size);
+  }
+
+  protected void beginHashMapIntGets(Integer size) {
+    map = new HashMap<Object, Object>();
+    testHashMapIntAdds(size);
+  }
+
+  protected void beginHashMapStringGets(Integer size) {
+    map = new HashMap<Object, Object>();
+    testHashMapStringAdds(size);
+  }
+
+  protected void initMap() {
+    map = new HashMap<Object, Object>();
+  }
+}
diff --git a/user/test/com/google/gwt/emultest/java/util/ListBenchmark.java b/user/test/com/google/gwt/emultest/java/util/ListBenchmark.java
new file mode 100644
index 0000000..9719853
--- /dev/null
+++ b/user/test/com/google/gwt/emultest/java/util/ListBenchmark.java
@@ -0,0 +1,231 @@
+/*
+ * 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.emultest.java.util;
+
+import com.google.gwt.junit.client.IntRange;
+import com.google.gwt.junit.client.Benchmark;
+import com.google.gwt.junit.client.Operator;
+import com.google.gwt.junit.client.annotations.RangeField;
+import com.google.gwt.junit.client.annotations.RangeEnum;
+import com.google.gwt.junit.client.annotations.Setup;
+
+import java.util.List;
+import java.util.Arrays;
+
+/**
+ * Benchmarks common operations on {@link List Lists}. This test covers
+ * appends, inserts, and removes for various sizes and positions.
+ * 
+ */
+public abstract class ListBenchmark extends Benchmark {
+
+  /* TODO(tobyr) Add more tests such as iteration, non-sequential 
+   * random access, and sublists.
+   */
+
+  private static final int PRIME = 3001;
+
+  /**
+   * The various positions that data can be inserted into a list.
+   */
+  protected enum Position {
+
+    BEGIN("at the beginning"),
+    EXPLICIT_END("explicitly at the end"),
+    IMPLICIT_END("implicitly at the end"),
+    VARIED("in varied locations");
+
+    private String label;
+
+    /**
+     * Constructor for <code>Position</code>.
+     *
+     * @param label a not <code>null</code> label describing this
+     * <code>Position</code>.
+     */
+    Position(String label) {
+      this.label = label;
+    }
+
+    /**
+     * Returns the textual description for the position.
+     *
+     * @return a not <code>null</code> description.
+     */
+    public String toString() {
+      return label;
+    }
+  }
+
+  protected final List<Position> explicitPositions = Arrays
+      .asList(Position.BEGIN, Position.EXPLICIT_END, Position.VARIED);
+
+  protected final IntRange insertRemoveRange = new IntRange(64,
+      Integer.MAX_VALUE, Operator.MULTIPLY, 2);
+
+  protected final IntRange baseRange = new IntRange(512, Integer.MAX_VALUE,
+      Operator.MULTIPLY, 2);
+
+  List<String> list;
+
+  int index = 0;
+
+  public String getModuleName() {
+    return "com.google.gwt.emultest.EmulSuite";
+  }
+
+  /**
+   * Appends <code>size</code> items to an empty {@code List}.
+   *
+   * @param size the size of the {@code List}
+   */
+  @Setup("beginListAdds")
+  public void testListAdds(@RangeField("baseRange") Integer size) {
+    int num = size.intValue();
+    for (int i = 0; i < num; i++) {
+      list.add("hello");
+    }
+  }
+
+  // Required for JUnit
+  public void testListAdds() {
+  }
+
+  /**
+   * Performs <code>size</code> gets on a {@code List} of size,
+   * <code>size</code>.
+   *
+   * @param size the size of the {@code List}
+   */
+  @Setup("beginListGets")
+  public void testListGets(@RangeField("baseRange") Integer size) {
+    int num = size.intValue();
+    for (int i = 0; i < num; i++) {
+      list.get(i);
+    }
+  }
+
+  // Required for JUnit
+  public void testListGets() {
+  }
+
+  /**
+   * Performs <code>size</code> inserts at position, <code>where</code>, on an
+   * empty <code>List</code>.
+   *
+   * @param where Where the inserts happen
+   * @param size The size of the <code>List</code>
+   *
+   */
+  @Setup("beginListInserts")
+  public void testListInserts(
+      @RangeEnum(Position.class)Position where,
+      @RangeField("insertRemoveRange")Integer size) {
+    insertIntoCollection(size, where, list);
+  }
+
+  // Required for JUnit
+  public void testListInserts() {
+  }
+
+  /**
+   * Performs <code>size</code> removes at position, <code>where</code>, on an
+   * ArrayList of size, <code>size</code>.
+   *
+   * @param where Where the inserts happen
+   * @param size The size of the <code>List</code>
+   */
+  @Setup("beginListRemoves")
+  public void testListRemoves(
+      @RangeField("explicitPositions")Position where,
+      @RangeField("insertRemoveRange")Integer size) {
+    removeFromCollection(size, where, list);
+  }
+
+  // Required for JUnit
+  public void testListRemoves() {
+  }
+
+  /**
+   * Creates a new empty List.
+   *
+   * @return a not <code>null</code>, empty List
+   */
+  protected abstract List<String> newList();
+
+  void beginListAdds(Integer size) {
+    list = newList();
+  }
+
+  void beginListGets(Integer size) {
+    createList(size);
+  }
+
+  void beginListInserts(Position where, Integer size) {
+    list = newList();
+    index = 0;
+  }
+
+  void beginListRemoves(Position where, Integer size) {
+    beginListInserts(where, size);
+    testListInserts(where, size);
+  }
+
+  private void createList(Integer size) {
+    beginListAdds(size);
+    testListAdds(size);
+  }
+
+  private void insertIntoCollection(Integer size, Position where,
+      List<String> l) {
+    int num = size.intValue();
+    for (int i = 0; i < num; i++) {
+      if (where == Position.IMPLICIT_END) {
+        l.add("hello");
+      } else if (where == Position.BEGIN) {
+        l.add(0, "hello");
+      } else if (where == Position.EXPLICIT_END) {
+        l.add(l.size(), "hello");
+      } else if (where == Position.VARIED) {
+        l.add(index, "hello");
+        index += PRIME;
+        index %= l.size();
+      }
+    }
+  }
+
+  private int removeFromCollection(Integer size, Position where,
+      List<String> l) {
+    int num = size.intValue();    
+    for (int i = 0; i < num; i++) {
+      if (where == Position.IMPLICIT_END) {
+        throw new RuntimeException("cannot remove from the end implicitly");
+      } else if (where == Position.BEGIN) {
+        l.remove(0);
+      } else if (where == Position.EXPLICIT_END) {
+        l.remove(l.size() - 1);
+      } else if (where == Position.VARIED) {
+        l.remove(index);
+        index += PRIME;
+        int currentSize = l.size();
+        if (currentSize > 0) {
+          index %= l.size();
+        }
+      }
+    }
+    return index;
+  }
+}
diff --git a/user/test/com/google/gwt/emultest/java/util/VectorBenchmark.java b/user/test/com/google/gwt/emultest/java/util/VectorBenchmark.java
new file mode 100644
index 0000000..e75bef7
--- /dev/null
+++ b/user/test/com/google/gwt/emultest/java/util/VectorBenchmark.java
@@ -0,0 +1,29 @@
+/*
+ * 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.emultest.java.util;
+
+import java.util.List;
+import java.util.Vector;
+
+/**
+ * A {@link ListBenchmark} for {@link Vector Vectors}.
+ */
+public class VectorBenchmark extends ListBenchmark {
+
+  protected List<String> newList() {
+    return new Vector<String>();
+  }
+}
diff --git a/user/test/com/google/gwt/junit/client/BenchmarkTest.java b/user/test/com/google/gwt/junit/client/BenchmarkTest.java
new file mode 100644
index 0000000..62787cb
--- /dev/null
+++ b/user/test/com/google/gwt/junit/client/BenchmarkTest.java
@@ -0,0 +1,152 @@
+/*
+ * 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.junit.client;
+
+import com.google.gwt.junit.client.annotations.RangeField;
+import com.google.gwt.junit.client.annotations.RangeEnum;
+import com.google.gwt.junit.client.annotations.Setup;
+import com.google.gwt.junit.client.annotations.Teardown;
+import com.google.gwt.junit.client.annotations.IterationTimeLimit;
+
+/**
+ * Basic Benchmark testing. 
+ *
+ */
+public class BenchmarkTest extends Benchmark {
+
+  /**
+   * Test enum.
+   */
+  protected enum TestEnum {
+    A, B, C;
+  }
+   
+  final String stringField = "foo";
+
+  final IntRange intRange = new IntRange(0, 20, Operator.ADD, 5);
+
+  final IntRange intRange2 = new IntRange(10, 1000, Operator.MULTIPLY, 10);
+
+  final IntRange veryLargeRange = new IntRange(0, Integer.MAX_VALUE,
+      Operator.ADD, 1);
+
+  String stateString = null;
+
+  long startTime;
+    
+  public String getModuleName() {
+    return "com.google.gwt.junit.JUnit";
+  }
+  
+  public void testEnumRange() {    
+  }
+  
+  /**
+   * Tests that we receive the enums in a range.
+   * 
+   * @param enumValue
+   */
+  @Setup("setupEnum")
+  @Teardown("teardownEnum")
+  public void testEnumRange(@RangeEnum(TestEnum.class) TestEnum enumValue) {
+    assertNotNull(enumValue);
+  }
+
+  /**
+   * Tests that a zero argument function works correctly.
+   * 
+   */
+  public void testNoParameters() {
+    assertEquals("foo", stringField);
+  }
+  
+  public void testOneParameterField() {    
+  }
+
+  /**
+   * Tests that a single argument function works correctly.
+   * 
+   * @param value
+   */
+  public void testOneParameterField(@RangeField("intRange") Integer value) {
+    assertTrue(value >= 0 && value <= 100 && value % 5 == 0);
+  }
+
+  /**
+   * Tests {@link Setup} and {@link Teardown}. 
+   * 
+   */
+  @Setup("setup")
+  @Teardown("teardown")
+  public void testSetupAndTeardown() {
+    assertEquals("setup", stateString);
+    stateString = "running";
+  }
+  
+  public void testTimeLimit() {    
+  }
+
+  /**
+   * Tests {@link @IterationTimeLimit}.
+   * 
+   * @param numIterations
+   */
+  @IterationTimeLimit(1L)
+  public void testTimeLimit(@RangeField("veryLargeRange") Integer numIterations) {
+
+    somethingExpensive();
+    
+    // Make sure we hit the time limit, instead of running through all 
+    // iterations.
+    assertTrue( numIterations < Integer.MAX_VALUE );
+  }
+  
+  public void testTwoParameterField() {    
+  }
+
+  /**
+   * Tests that a multiple argument function works correctly.
+   *
+   */
+  public void testTwoParameterField(@RangeField("intRange") Integer intOne, 
+      @RangeField("intRange2") Integer intTwo) {
+    assertTrue(intOne >= 0 && intOne <= 100 && intOne % 5 == 0);
+    assertTrue(intTwo >= 10 && intTwo <= 1000 && intTwo % 10 == 0);
+  }
+  
+  protected void setup() {
+    assertNull(stateString);
+    stateString = "setup";
+  }
+  
+  protected void teardown() {
+    assertNotNull(stateString);
+    assertTrue(stateString.equals("running") || stateString.equals("setup"));
+    stateString = null;
+  }
+
+  /**
+   * Do something that is relatively expensive both in hosted mode 
+   * and web mode. 
+   */
+  private native void somethingExpensive() /*-{
+    var deadField = 0;
+    for (var i = 0; i < 10000; ++i) {
+      deadField += Math.pow(deadField, i);
+    }
+  }-*/;  
+}