Emulate new methods for java.util.Map in Java 8.

Change-Id: I492cb49faf2b839cf7939d3eabae3c2a3fdfeb54
diff --git a/user/super/com/google/gwt/emul/java/util/Map.java b/user/super/com/google/gwt/emul/java/util/Map.java
index 0ab78c4..c5cccd2 100644
--- a/user/super/com/google/gwt/emul/java/util/Map.java
+++ b/user/super/com/google/gwt/emul/java/util/Map.java
@@ -15,6 +15,14 @@
  */
 package java.util;
 
+import static javaemul.internal.InternalPreconditions.checkCriticalNotNull;
+import static javaemul.internal.InternalPreconditions.checkNotNull;
+
+import java.io.Serializable;
+import java.util.function.BiConsumer;
+import java.util.function.BiFunction;
+import java.util.function.Function;
+
 /**
  * Abstract interface for maps.
  *
@@ -26,7 +34,7 @@
   /**
    * Represents an individual map entry.
    */
-  public interface Entry<K, V> {
+  interface Entry<K, V> {
     @Override
     boolean equals(Object o);
 
@@ -38,10 +46,71 @@
     int hashCode();
 
     V setValue(V value);
+
+    static <K extends Comparable<? super K>, V> Comparator<Map.Entry<K,V>> comparingByKey() {
+      return comparingByKey(Comparator.naturalOrder());
+    }
+
+    static <K, V> Comparator<Map.Entry<K, V>> comparingByKey(Comparator<? super K> cmp) {
+      checkCriticalNotNull(cmp);
+      return (Comparator<Map.Entry<K, V>> & Serializable)
+          (a, b) -> cmp.compare(a.getKey(), b.getKey());
+    }
+
+    static <K, V extends Comparable<? super V>> Comparator<Map.Entry<K,V>> comparingByValue() {
+      return comparingByValue(Comparator.naturalOrder());
+    }
+
+    static <K, V> Comparator<Map.Entry<K, V>> comparingByValue(Comparator<? super V> cmp) {
+      checkCriticalNotNull(cmp);
+      return (Comparator<Map.Entry<K, V>> & Serializable)
+          (a, b) -> cmp.compare(a.getValue(), b.getValue());
+    }
   }
 
   void clear();
 
+  default V compute(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
+    checkNotNull(remappingFunction);
+
+    V value = remappingFunction.apply(key, get(key));
+    if (value != null) {
+      put(key, value);
+    } else {
+      remove(key);
+    }
+    return value;
+  }
+
+  default V computeIfAbsent(K key, Function<? super K, ? extends V> remappingFunction) {
+    checkNotNull(remappingFunction);
+
+    V value = get(key);
+    if (value == null) {
+      value = remappingFunction.apply(key);
+      if (value != null) {
+        put(key, value);
+      }
+    }
+    return value;
+  }
+
+  default V computeIfPresent(K key,
+                             BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
+    checkNotNull(remappingFunction);
+
+    V value = get(key);
+    if (value != null) {
+      value = remappingFunction.apply(key, value);
+      if (value != null) {
+        put(key, value);
+      } else {
+        remove(key);
+      }
+    }
+    return value;
+  }
+
   boolean containsKey(Object key);
 
   boolean containsValue(Object value);
@@ -51,8 +120,20 @@
   @Override
   boolean equals(Object o);
 
+  default void forEach(BiConsumer<? super K, ? super V> consumer) {
+    checkNotNull(consumer);
+    for (Entry<K, V> entry : entrySet()) {
+      consumer.accept(entry.getKey(), entry.getValue());
+    }
+  }
+
   V get(Object key);
 
+  default V getOrDefault(Object key, V defaultValue) {
+    V currentValue = get(key);
+    return (currentValue == null && !containsKey(key)) ? defaultValue : currentValue;
+  }
+
   @Override
   int hashCode();
 
@@ -60,12 +141,60 @@
 
   Set<K> keySet();
 
+  default V merge(K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
+    checkNotNull(remappingFunction);
+    checkNotNull(value);
+
+    V currentValue = get(key);
+    V newValue = currentValue == null ? value : remappingFunction.apply(currentValue, value);
+    if (newValue == null) {
+      remove(key);
+    } else {
+      put(key, newValue);
+    }
+    return newValue;
+  }
+
   V put(K key, V value);
 
+  default V putIfAbsent(K key, V value) {
+    V currentValue = get(key);
+    return currentValue != null ? currentValue : put(key, value);
+  }
+
   void putAll(Map<? extends K, ? extends V> t);
 
   V remove(Object key);
 
+  default boolean remove(Object key, Object value) {
+    Object currentValue = get(key);
+    if (!Objects.equals(currentValue, value) || (currentValue == null && !containsKey(key))) {
+      return false;
+    }
+    remove(key);
+    return true;
+  }
+
+  default V replace(K key, V value) {
+    return containsKey(key) ? put(key, value) : null;
+  }
+
+  default boolean replace(K key, V oldValue, V newValue) {
+    Object currentValue = get(key);
+    if (!Objects.equals(currentValue, oldValue) || (currentValue == null && !containsKey(key))) {
+      return false;
+    }
+    put(key, newValue);
+    return true;
+  }
+
+  default void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {
+    checkNotNull(function);
+    for (Entry<K, V> entry : entrySet()) {
+      entry.setValue(function.apply(entry.getKey(), entry.getValue()));
+    }
+  }
+
   int size();
 
   Collection<V> values();
diff --git a/user/test/com/google/gwt/emultest/EmulJava8Suite.java b/user/test/com/google/gwt/emultest/EmulJava8Suite.java
index 0d90d04..fdf0c33 100644
--- a/user/test/com/google/gwt/emultest/EmulJava8Suite.java
+++ b/user/test/com/google/gwt/emultest/EmulJava8Suite.java
@@ -17,8 +17,13 @@
 
 import com.google.gwt.emultest.java8.util.ComparatorTest;
 import com.google.gwt.emultest.java8.util.DoubleSummaryStatisticsTest;
+import com.google.gwt.emultest.java8.util.HashMapTest;
+import com.google.gwt.emultest.java8.util.IdentityHashMapTest;
 import com.google.gwt.emultest.java8.util.IntSummaryStatisticsTest;
+import com.google.gwt.emultest.java8.util.LinkedHashMapTest;
 import com.google.gwt.emultest.java8.util.LongSummaryStatisticsTest;
+import com.google.gwt.emultest.java8.util.MapEntryTest;
+import com.google.gwt.emultest.java8.util.MapTest;
 import com.google.gwt.emultest.java8.util.OptionalDoubleTest;
 import com.google.gwt.emultest.java8.util.OptionalIntTest;
 import com.google.gwt.emultest.java8.util.OptionalLongTest;
@@ -26,6 +31,7 @@
 import com.google.gwt.emultest.java8.util.PrimitiveIteratorTest;
 import com.google.gwt.emultest.java8.util.SpliteratorsTest;
 import com.google.gwt.emultest.java8.util.StringJoinerTest;
+import com.google.gwt.emultest.java8.util.TreeMapTest;
 import com.google.gwt.junit.tools.GWTTestSuite;
 
 import junit.framework.Test;
@@ -40,6 +46,12 @@
 
     //-- java.util
     suite.addTestSuite(ComparatorTest.class);
+    suite.addTestSuite(MapTest.class);
+    suite.addTestSuite(MapEntryTest.class);
+    suite.addTestSuite(HashMapTest.class);
+    suite.addTestSuite(IdentityHashMapTest.class);
+    suite.addTestSuite(LinkedHashMapTest.class);
+    suite.addTestSuite(TreeMapTest.class);
     suite.addTestSuite(OptionalTest.class);
     suite.addTestSuite(OptionalIntTest.class);
     suite.addTestSuite(OptionalLongTest.class);
diff --git a/user/test/com/google/gwt/emultest/java8/util/AbstractJava8MapTest.java b/user/test/com/google/gwt/emultest/java8/util/AbstractJava8MapTest.java
new file mode 100644
index 0000000..2ce7b9a
--- /dev/null
+++ b/user/test/com/google/gwt/emultest/java8/util/AbstractJava8MapTest.java
@@ -0,0 +1,258 @@
+/*
+ * Copyright 2016 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.java8.util;
+
+import com.google.gwt.emultest.java.util.EmulTestBase;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+
+/**
+ * Tests for java.util.Map implementing classes Java 8 API emulation.
+ */
+abstract class AbstractJava8MapTest extends EmulTestBase {
+
+  private Map<String, String> testSample;
+
+  @Override
+  protected void gwtSetUp() throws Exception {
+    super.gwtSetUp();
+    testSample = createTestSample();
+  }
+
+  public void testCompute() {
+    Map<String, String> map = createTestMap();
+
+    String value = map.compute("a", (k, v) -> k + " - " + v);
+    assertEquals("a - A", value);
+    assertTrue(map.containsKey("a"));
+    assertEquals("a - A", map.get("a"));
+
+    value = map.compute("a", (k, v) -> null);
+    assertNull(value);
+    assertFalse(map.containsKey("a"));
+
+    value = map.compute("a", (k, v) -> {
+      assertNull(v);
+      return k.toUpperCase();
+    });
+    assertEquals("A", value);
+    assertTrue(map.containsKey("a"));
+    assertEquals("A", map.get("a"));
+  }
+
+  public void testComputeIfAbsent() {
+    Map<String, String> map = createTestMap();
+
+    String value = map.computeIfAbsent("a", k -> {
+      fail();
+      return null;
+    });
+    assertEquals("A", value);
+    assertTrue(map.containsKey("a"));
+    assertEquals("A", map.get("a"));
+
+    map.remove("a");
+    value = map.computeIfAbsent("a", String::toUpperCase);
+    assertEquals("A", value);
+    assertTrue(map.containsKey("a"));
+    assertEquals("A", map.get("a"));
+
+    map.remove("a");
+    value = map.computeIfAbsent("a", k -> null);
+    assertNull(value);
+    assertFalse(map.containsKey("a"));
+  }
+
+  public void testComputeIfPresent() {
+    Map<String, String> map = createTestMap();
+
+    String value = map.computeIfPresent("a", (k, v) -> k + " - " + v);
+    assertEquals("a - A", value);
+    assertTrue(map.containsKey("a"));
+    assertEquals("a - A", map.get("a"));
+
+    value = map.computeIfPresent("a", (k, v) -> null);
+    assertNull(value);
+    assertFalse(map.containsKey("a"));
+
+    value = map.computeIfPresent("a", (k, v) -> {
+      fail();
+      return null;
+    });
+    assertNull(value);
+    assertFalse(map.containsKey("a"));
+  }
+
+  public void testForeach() {
+    Map<String, String> map = createTestMap();
+    Map<String, String> expected = new HashMap<>(testSample);
+
+    assertEquals(expected.size(), map.size());
+    map.forEach((k, v) -> {
+      assertTrue(expected.containsKey(k));
+      assertEquals(expected.get(k), v);
+      expected.remove(k);
+    });
+    assertTrue(expected.isEmpty());
+  }
+
+  public void testGetOrDefault() {
+    Map<String, String> map = createTestMap();
+
+    String value = map.getOrDefault("a", null);
+    assertEquals("A", value);
+    assertTrue(map.containsKey("a"));
+    assertEquals("A", map.get("a"));
+
+    map.remove("a");
+    value = map.getOrDefault("a", "A");
+    assertEquals("A", value);
+    assertFalse(map.containsKey("a"));
+    assertNull(map.get("a"));
+
+    map.put("a", null);
+    value = map.getOrDefault("a", "A");
+    assertNull(value);
+    assertTrue(map.containsKey("a"));
+    assertNull(map.get("a"));
+  }
+
+  public void testMerge() {
+    Map<String, String> map = createTestMap();
+
+    String newValue = map.merge("a", "a", (currentValue, value) -> {
+      assertEquals("A", currentValue);
+      assertEquals("a", value);
+      return value;
+    });
+    assertEquals(newValue, "a");
+    assertTrue(map.containsKey("a"));
+    assertEquals("a", map.get("a"));
+
+    try {
+      map.merge("a", null, (currentValue, value) -> "");
+      fail();
+    } catch (NullPointerException expected) {
+    }
+
+    newValue = map.merge("a", "", (currentValue, value) -> null);
+    assertNull(newValue);
+    assertFalse(map.containsKey("a"));
+    assertNull(map.get("a"));
+
+    newValue = map.merge("a", "A", (currentValue, value) -> value);
+    assertEquals("A", newValue);
+    assertTrue(map.containsKey("a"));
+    assertEquals("A", map.get("a"));
+  }
+
+  public void testPutIfAbsent() {
+    Map<String, String> map = createTestMap();
+
+    String oldValue = map.putIfAbsent("a", "a");
+    assertEquals("A", oldValue);
+    assertTrue(map.containsKey("a"));
+    assertEquals("A", map.get("a"));
+
+    map.remove("a");
+    oldValue = map.putIfAbsent("a", "a");
+    assertNull(oldValue);
+    assertTrue(map.containsKey("a"));
+    assertEquals("a", map.get("a"));
+  }
+
+  public void testRemove() {
+    Map<String, String> map = createTestMap();
+
+    assertFalse(map.remove("a", "a"));
+    assertTrue(map.containsKey("a"));
+    assertEquals("A", map.get("a"));
+
+    assertTrue(map.remove("a", "A"));
+    assertFalse(map.containsKey("a"));
+
+    assertFalse(map.remove("a", null));
+
+    map.put("a", null);
+    assertTrue(map.remove("a", null));
+    assertFalse(map.containsKey("a"));
+  }
+
+  public void testReplace() {
+    Map<String, String> map = createTestMap();
+
+    String oldValue = map.replace("a", "a");
+    assertEquals("A", oldValue);
+    assertTrue(map.containsKey("a"));
+    assertEquals("a", map.get("a"));
+
+    map.remove("a");
+    oldValue = map.replace("a", "A");
+    assertNull(oldValue);
+    assertFalse(map.containsKey("a"));
+    assertNull(map.get("a"));
+  }
+
+  public void testReplace_Key_OldValue_NewValue() {
+    Map<String, String> map = createTestMap();
+
+    assertTrue(map.replace("a", "A", "a"));
+    assertTrue(map.containsKey("a"));
+    assertEquals("a", map.get("a"));
+
+    assertFalse(map.replace("a", "A", "a"));
+
+    assertTrue(map.replace("a", "a", null));
+    assertTrue(map.containsKey("a"));
+    assertNull(map.get("a"));
+
+    map.remove("a");
+    assertFalse(map.replace("a", "a", "A"));
+    assertFalse(map.containsKey("a"));
+    assertNull(map.get("a"));
+  }
+
+  public void testReplaceAll() {
+    Map<String, String> map = createTestMap();
+    map.replaceAll((k, v) -> v.toLowerCase());
+
+    assertEquals(testSample.size(), map.size());
+    for (Entry<String, String> entry : testSample.entrySet()) {
+      assertTrue(map.containsKey(entry.getKey()));
+      assertEquals(map.get(entry.getKey()), entry.getValue().toLowerCase());
+    }
+  }
+
+  private Map<String, String> createTestMap() {
+    Map<String, String> map = createMap();
+    map.putAll(testSample);
+    return map;
+  }
+
+  private static Map<String, String> createTestSample() {
+    Map<String, String> map = new HashMap<>();
+    map.put("a", "A");
+    map.put("b", "B");
+    map.put("c", "C");
+    return Collections.unmodifiableMap(map);
+  }
+
+  protected abstract Map<String, String> createMap();
+
+}
diff --git a/user/test/com/google/gwt/emultest/java8/util/HashMapTest.java b/user/test/com/google/gwt/emultest/java8/util/HashMapTest.java
new file mode 100644
index 0000000..993e97d
--- /dev/null
+++ b/user/test/com/google/gwt/emultest/java8/util/HashMapTest.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2016 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.java8.util;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Tests for java.util.HashMap Java 8 API emulation.
+ */
+public class HashMapTest extends AbstractJava8MapTest {
+
+  @Override
+  protected Map<String, String> createMap() {
+    return new HashMap<>();
+  }
+}
diff --git a/user/test/com/google/gwt/emultest/java8/util/IdentityHashMapTest.java b/user/test/com/google/gwt/emultest/java8/util/IdentityHashMapTest.java
new file mode 100644
index 0000000..c20fffa
--- /dev/null
+++ b/user/test/com/google/gwt/emultest/java8/util/IdentityHashMapTest.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2016 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.java8.util;
+
+import java.util.IdentityHashMap;
+import java.util.Map;
+
+/**
+ * Tests for java.util.IdentityHashMap Java 8 API emulation.
+ */
+public class IdentityHashMapTest extends AbstractJava8MapTest {
+
+  @Override
+  protected Map<String, String> createMap() {
+    return new IdentityHashMap<>();
+  }
+}
diff --git a/user/test/com/google/gwt/emultest/java8/util/LinkedHashMapTest.java b/user/test/com/google/gwt/emultest/java8/util/LinkedHashMapTest.java
new file mode 100644
index 0000000..7b7b949
--- /dev/null
+++ b/user/test/com/google/gwt/emultest/java8/util/LinkedHashMapTest.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2016 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.java8.util;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+ * Tests for java.util.LinkedHashMap Java 8 API emulation.
+ */
+public class LinkedHashMapTest extends AbstractJava8MapTest {
+
+  @Override
+  protected Map<String, String> createMap() {
+    return new LinkedHashMap<>();
+  }
+}
diff --git a/user/test/com/google/gwt/emultest/java8/util/MapEntryTest.java b/user/test/com/google/gwt/emultest/java8/util/MapEntryTest.java
new file mode 100644
index 0000000..09a4d91
--- /dev/null
+++ b/user/test/com/google/gwt/emultest/java8/util/MapEntryTest.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2016 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.java8.util;
+
+import com.google.gwt.emultest.java.util.EmulTestBase;
+
+import java.util.AbstractMap.SimpleImmutableEntry;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Map.Entry;
+
+/**
+ * Tests for java.util.Map.Entry Java 8 API emulation.
+ */
+public class MapEntryTest extends EmulTestBase {
+
+  private Entry<String, String> entry1;
+  private Entry<String, String> entry2;
+
+  @Override
+  protected void gwtSetUp() throws Exception {
+    super.gwtSetUp();
+    entry1 = createTestEntry1();
+    entry2 = createTestEntry2();
+  }
+
+  public void testEntryComparingByKey() {
+    final Comparator<Entry<String, String>> entryComparator = Entry.comparingByKey();
+
+    assertEquals(-1, entryComparator.compare(entry1, entry2));
+    assertEquals(1, entryComparator.compare(entry2, entry1));
+    assertEquals(0, entryComparator.compare(entry1, entry1));
+    assertEquals(0, entryComparator.compare(entry2, entry2));
+    assertEquals(0, entryComparator.compare(entry1, createTestEntry1()));
+    assertEquals(0, entryComparator.compare(entry2, createTestEntry2()));
+  }
+
+  public void testEntryComparingByKeyWithComparator() {
+    final Comparator<Entry<String, String>> entryComparator =
+        Entry.comparingByKey(Collections.reverseOrder());
+
+    assertEquals(1, entryComparator.compare(entry1, entry2));
+    assertEquals(-1, entryComparator.compare(entry2, entry1));
+    assertEquals(0, entryComparator.compare(entry1, entry1));
+    assertEquals(0, entryComparator.compare(entry2, entry2));
+    assertEquals(0, entryComparator.compare(entry1, createTestEntry1()));
+    assertEquals(0, entryComparator.compare(entry2, createTestEntry2()));
+  }
+
+  public void testEntryComparingByValue() {
+    final Comparator<Entry<String, String>> valueComparator = Entry.comparingByValue();
+
+    assertEquals(-1, valueComparator.compare(entry1, entry2));
+    assertEquals(1, valueComparator.compare(entry2, entry1));
+    assertEquals(0, valueComparator.compare(entry1, entry1));
+    assertEquals(0, valueComparator.compare(entry2, entry2));
+    assertEquals(0, valueComparator.compare(entry1, createTestEntry1()));
+    assertEquals(0, valueComparator.compare(entry2, createTestEntry2()));
+  }
+
+  public void testEntryComparingByValueWithComparator() {
+    final Comparator<Entry<String, String>> valueComparator =
+        Entry.comparingByValue(Collections.reverseOrder());
+
+    assertEquals(1, valueComparator.compare(entry1, entry2));
+    assertEquals(-1, valueComparator.compare(entry2, entry1));
+    assertEquals(0, valueComparator.compare(entry1, entry1));
+    assertEquals(0, valueComparator.compare(entry2, entry2));
+    assertEquals(0, valueComparator.compare(entry1, createTestEntry1()));
+    assertEquals(0, valueComparator.compare(entry2, createTestEntry2()));
+  }
+
+  private static Entry<String, String> createTestEntry1() {
+    return new SimpleImmutableEntry<>("a", "A");
+  }
+
+  private static Entry<String, String> createTestEntry2() {
+    return new SimpleImmutableEntry<>("b", "B");
+  }
+}
diff --git a/user/test/com/google/gwt/emultest/java8/util/MapTest.java b/user/test/com/google/gwt/emultest/java8/util/MapTest.java
new file mode 100644
index 0000000..febb052
--- /dev/null
+++ b/user/test/com/google/gwt/emultest/java8/util/MapTest.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2016 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.java8.util;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Tests for java.util.Map Java 8 API emulation.
+ */
+public class MapTest extends AbstractJava8MapTest {
+
+  @Override
+  protected Map<String, String> createMap() {
+    return new TestMap<>();
+  }
+
+  private static class TestMap<K, V> implements Map<K, V> {
+    private final Map<K, V> container = new HashMap<>();
+
+    @Override
+    public int size() {
+      return container.size();
+    }
+
+    @Override
+    public boolean isEmpty() {
+      return container.isEmpty();
+    }
+
+    @Override
+    public boolean containsKey(Object key) {
+      return container.containsKey(key);
+    }
+
+    @Override
+    public boolean containsValue(Object value) {
+      return container.containsValue(value);
+    }
+
+    @Override
+    public V get(Object key) {
+      return container.get(key);
+    }
+
+    @Override
+    public V put(K key, V value) {
+      return container.put(key, value);
+    }
+
+    @Override
+    public V remove(Object key) {
+      return container.remove(key);
+    }
+
+    @Override
+    public void putAll(Map<? extends K, ? extends V> m) {
+      container.putAll(m);
+    }
+
+    @Override
+    public void clear() {
+      container.clear();
+    }
+
+    @Override
+    public Set<K> keySet() {
+      return container.keySet();
+    }
+
+    @Override
+    public Collection<V> values() {
+      return container.values();
+    }
+
+    @Override
+    public Set<Entry<K, V>> entrySet() {
+      return container.entrySet();
+    }
+  }
+}
diff --git a/user/test/com/google/gwt/emultest/java8/util/TreeMapTest.java b/user/test/com/google/gwt/emultest/java8/util/TreeMapTest.java
new file mode 100644
index 0000000..62b24aa
--- /dev/null
+++ b/user/test/com/google/gwt/emultest/java8/util/TreeMapTest.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2016 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.java8.util;
+
+import java.util.Map;
+import java.util.TreeMap;
+
+/**
+ * Tests for java.util.TreeMap Java 8 API emulation.
+ */
+public class TreeMapTest extends AbstractJava8MapTest {
+
+  @Override
+  protected Map<String, String> createMap() {
+    return new TreeMap<>();
+  }
+}