MapsTest.java revision 7dd252788645e940eada959bdde927426e2531c9
1/*
2 * Copyright (C) 2007 The Guava Authors
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.google.common.collect;
18
19import static com.google.common.collect.Maps.transformEntries;
20import static com.google.common.collect.Maps.transformValues;
21import static com.google.common.collect.testing.Helpers.mapEntry;
22
23import com.google.common.annotations.GwtCompatible;
24import com.google.common.annotations.GwtIncompatible;
25import com.google.common.base.Equivalence;
26import com.google.common.base.Function;
27import com.google.common.base.Functions;
28import com.google.common.base.Predicate;
29import com.google.common.base.Predicates;
30import com.google.common.collect.Maps.EntryTransformer;
31import com.google.common.collect.Maps.ValueDifferenceImpl;
32import com.google.common.collect.SetsTest.Derived;
33import com.google.common.testing.EqualsTester;
34import com.google.common.testing.NullPointerTester;
35
36import junit.framework.TestCase;
37
38import org.truth0.Truth;
39import org.truth0.subjects.CollectionSubject;
40
41import java.io.IOException;
42import java.io.StringBufferInputStream;
43import java.lang.reflect.Field;
44import java.util.Arrays;
45import java.util.Collection;
46import java.util.Collections;
47import java.util.Comparator;
48import java.util.EnumMap;
49import java.util.Enumeration;
50import java.util.HashMap;
51import java.util.IdentityHashMap;
52import java.util.Iterator;
53import java.util.LinkedHashMap;
54import java.util.List;
55import java.util.Map;
56import java.util.Map.Entry;
57import java.util.Properties;
58import java.util.Set;
59import java.util.SortedMap;
60import java.util.SortedSet;
61import java.util.TreeMap;
62import java.util.concurrent.ConcurrentMap;
63
64/**
65 * Unit test for {@code Maps}.
66 *
67 * @author Kevin Bourrillion
68 * @author Mike Bostock
69 * @author Jared Levy
70 */
71@GwtCompatible(emulated = true)
72public class MapsTest extends TestCase {
73
74  private static final Comparator<Integer> SOME_COMPARATOR = Collections.reverseOrder();
75
76  public void testHashMap() {
77    HashMap<Integer, Integer> map = Maps.newHashMap();
78    assertEquals(Collections.emptyMap(), map);
79  }
80
81  public void testHashMapWithInitialMap() {
82    Map<String, Integer> original = new TreeMap<String, Integer>();
83    original.put("a", 1);
84    original.put("b", 2);
85    original.put("c", 3);
86    HashMap<String, Integer> map = Maps.newHashMap(original);
87    assertEquals(original, map);
88  }
89
90  public void testHashMapGeneralizesTypes() {
91    Map<String, Integer> original = new TreeMap<String, Integer>();
92    original.put("a", 1);
93    original.put("b", 2);
94    original.put("c", 3);
95    HashMap<Object, Object> map = Maps
96        .newHashMap((Map<? extends Object, ? extends Object>) original);
97    assertEquals(original, map);
98  }
99
100  public void testCapacityForNegativeSizeFails() {
101    try {
102      Maps.capacity(-1);
103      fail("Negative expected size must result in IllegalArgumentException");
104    } catch (IllegalArgumentException ex) {}
105  }
106
107  /**
108   * Tests that nHMWES makes hash maps large enough that adding the expected
109   * number of elements won't cause a rehash.
110   *
111   * This test may fail miserably on non-OpenJDK environments...
112   */
113  @GwtIncompatible("reflection")
114  public void notestNewHashMapWithExpectedSize_wontGrow() throws Exception {
115    for (int size = 0; size < 200; size++) {
116      HashMap<Integer, Void> map1 = Maps.newHashMapWithExpectedSize(size);
117
118      int startSize = sizeOf(map1);
119
120      for (int i = 0; i < size; i++) {
121        map1.put(i, null);
122      }
123      assertEquals("table size after adding " + size + "elements", startSize, sizeOf(map1));
124
125      /*
126       * Something slightly different happens when the entries are added all at
127       * once; make sure that passes too.
128       */
129      HashMap<Integer, Void> map2 = Maps.newHashMapWithExpectedSize(size);
130      map2.putAll(map1);
131      assertEquals("table size after adding " + size + "elements", startSize, sizeOf(map2));
132    }
133  }
134
135  @GwtIncompatible("reflection")
136  private static int sizeOf(HashMap<?, ?> hashMap) throws Exception {
137    Field tableField = HashMap.class.getDeclaredField("table");
138    tableField.setAccessible(true);
139    Object[] table = (Object[]) tableField.get(hashMap);
140    return table.length;
141  }
142
143  public void testCapacityForLargeSizes() {
144    int[] largeExpectedSizes = new int[] { Integer.MAX_VALUE / 2 - 1, Integer.MAX_VALUE / 2,
145        Integer.MAX_VALUE / 2 + 1, Integer.MAX_VALUE - 1, Integer.MAX_VALUE };
146    for (int expectedSize : largeExpectedSizes) {
147      int capacity = Maps.capacity(expectedSize);
148      assertTrue("capacity (" + capacity + ") must be >= expectedSize (" + expectedSize + ")",
149          capacity >= expectedSize);
150    }
151  }
152
153  public void testLinkedHashMap() {
154    LinkedHashMap<Integer, Integer> map = Maps.newLinkedHashMap();
155    assertEquals(Collections.emptyMap(), map);
156  }
157
158  @SuppressWarnings("serial")
159  public void testLinkedHashMapWithInitialMap() {
160    Map<String, String> map = new LinkedHashMap<String, String>() {
161      {
162        put("Hello", "World");
163        put("first", "second");
164        put("polygene", "lubricants");
165        put("alpha", "betical");
166      }
167    };
168
169    LinkedHashMap<String, String> copy = Maps.newLinkedHashMap(map);
170
171    Iterator<Entry<String, String>> iter = copy.entrySet().iterator();
172    assertTrue(iter.hasNext());
173    Entry<String, String> entry = iter.next();
174    assertEquals("Hello", entry.getKey());
175    assertEquals("World", entry.getValue());
176    assertTrue(iter.hasNext());
177
178    entry = iter.next();
179    assertEquals("first", entry.getKey());
180    assertEquals("second", entry.getValue());
181    assertTrue(iter.hasNext());
182
183    entry = iter.next();
184    assertEquals("polygene", entry.getKey());
185    assertEquals("lubricants", entry.getValue());
186    assertTrue(iter.hasNext());
187
188    entry = iter.next();
189    assertEquals("alpha", entry.getKey());
190    assertEquals("betical", entry.getValue());
191    assertFalse(iter.hasNext());
192  }
193
194  public void testLinkedHashMapGeneralizesTypes() {
195    Map<String, Integer> original = new LinkedHashMap<String, Integer>();
196    original.put("a", 1);
197    original.put("b", 2);
198    original.put("c", 3);
199    HashMap<Object, Object> map = Maps.<Object, Object>newLinkedHashMap(original);
200    assertEquals(original, map);
201  }
202
203  public void testIdentityHashMap() {
204    IdentityHashMap<Integer, Integer> map = Maps.newIdentityHashMap();
205    assertEquals(Collections.emptyMap(), map);
206  }
207
208  public void testConcurrentMap() {
209    ConcurrentMap<Integer, Integer> map = Maps.newConcurrentMap();
210    assertEquals(Collections.emptyMap(), map);
211  }
212
213  public void testTreeMap() {
214    TreeMap<Integer, Integer> map = Maps.newTreeMap();
215    assertEquals(Collections.emptyMap(), map);
216    assertNull(map.comparator());
217  }
218
219  public void testTreeMapDerived() {
220    TreeMap<Derived, Integer> map = Maps.newTreeMap();
221    assertEquals(Collections.emptyMap(), map);
222    map.put(new Derived("foo"), 1);
223    map.put(new Derived("bar"), 2);
224    ASSERT.that(map.keySet()).has().allOf(new Derived("bar"), new Derived("foo")).inOrder();
225    ASSERT.that(map.values()).has().allOf(2, 1).inOrder();
226    assertNull(map.comparator());
227  }
228
229  public void testTreeMapNonGeneric() {
230    TreeMap<LegacyComparable, Integer> map = Maps.newTreeMap();
231    assertEquals(Collections.emptyMap(), map);
232    map.put(new LegacyComparable("foo"), 1);
233    map.put(new LegacyComparable("bar"), 2);
234    ASSERT.that(map.keySet()).has().allOf(new LegacyComparable("bar"), new LegacyComparable("foo"))
235        .inOrder();
236    ASSERT.that(map.values()).has().allOf(2, 1).inOrder();
237    assertNull(map.comparator());
238  }
239
240  public void testTreeMapWithComparator() {
241    TreeMap<Integer, Integer> map = Maps.<Integer, Integer, Integer>newTreeMap(SOME_COMPARATOR);
242    assertEquals(Collections.emptyMap(), map);
243    assertSame(SOME_COMPARATOR, map.comparator());
244  }
245
246  public void testTreeMapWithInitialMap() {
247    SortedMap<Integer, Integer> map = Maps.newTreeMap();
248    map.put(5, 10);
249    map.put(3, 20);
250    map.put(1, 30);
251    TreeMap<Integer, Integer> copy = Maps.newTreeMap(map);
252    assertEquals(copy, map);
253    assertSame(copy.comparator(), map.comparator());
254  }
255
256  public enum SomeEnum {
257    SOME_INSTANCE
258  }
259
260  public void testEnumMap() {
261    EnumMap<SomeEnum, Integer> map = Maps.newEnumMap(SomeEnum.class);
262    assertEquals(Collections.emptyMap(), map);
263    map.put(SomeEnum.SOME_INSTANCE, 0);
264    assertEquals(Collections.singletonMap(SomeEnum.SOME_INSTANCE, 0), map);
265  }
266
267  public void testEnumMapNullClass() {
268    try {
269      Maps.<SomeEnum, Long>newEnumMap((Class<MapsTest.SomeEnum>) null);
270      fail("no exception thrown");
271    } catch (NullPointerException expected) {}
272  }
273
274  public void testEnumMapWithInitialEnumMap() {
275    EnumMap<SomeEnum, Integer> original = Maps.newEnumMap(SomeEnum.class);
276    original.put(SomeEnum.SOME_INSTANCE, 0);
277    EnumMap<SomeEnum, Integer> copy = Maps.newEnumMap(original);
278    assertEquals(original, copy);
279  }
280
281  public void testEnumMapWithInitialEmptyEnumMap() {
282    EnumMap<SomeEnum, Integer> original = Maps.newEnumMap(SomeEnum.class);
283    EnumMap<SomeEnum, Integer> copy = Maps.newEnumMap(original);
284    assertEquals(original, copy);
285    assertNotSame(original, copy);
286  }
287
288  public void testEnumMapWithInitialMap() {
289    HashMap<SomeEnum, Integer> original = Maps.newHashMap();
290    original.put(SomeEnum.SOME_INSTANCE, 0);
291    EnumMap<SomeEnum, Integer> copy = Maps.newEnumMap(original);
292    assertEquals(original, copy);
293  }
294
295  public void testEnumMapWithInitialEmptyMap() {
296    Map<SomeEnum, Integer> original = Maps.newHashMap();
297    try {
298      Maps.newEnumMap(original);
299      fail("Empty map must result in an IllegalArgumentException");
300    } catch (IllegalArgumentException expected) {}
301  }
302
303  public void testToStringImplWithNullKeys() throws Exception {
304    Map<String, String> hashmap = Maps.newHashMap();
305    hashmap.put("foo", "bar");
306    hashmap.put(null, "baz");
307
308    assertEquals(hashmap.toString(), Maps.toStringImpl(hashmap));
309  }
310
311  public void testToStringImplWithNullValues() throws Exception {
312    Map<String, String> hashmap = Maps.newHashMap();
313    hashmap.put("foo", "bar");
314    hashmap.put("baz", null);
315
316    assertEquals(hashmap.toString(), Maps.toStringImpl(hashmap));
317  }
318
319  @GwtIncompatible("NullPointerTester")
320  public void testNullPointerExceptions() {
321    new NullPointerTester().testAllPublicStaticMethods(Maps.class);
322  }
323
324  private static final Map<Integer, Integer> EMPTY = Collections.emptyMap();
325  private static final Map<Integer, Integer> SINGLETON = Collections.singletonMap(1, 2);
326
327  public void testMapDifferenceEmptyEmpty() {
328    MapDifference<Integer, Integer> diff = Maps.difference(EMPTY, EMPTY);
329    assertTrue(diff.areEqual());
330    assertEquals(EMPTY, diff.entriesOnlyOnLeft());
331    assertEquals(EMPTY, diff.entriesOnlyOnRight());
332    assertEquals(EMPTY, diff.entriesInCommon());
333    assertEquals(EMPTY, diff.entriesDiffering());
334    assertEquals("equal", diff.toString());
335  }
336
337  public void testMapDifferenceEmptySingleton() {
338    MapDifference<Integer, Integer> diff = Maps.difference(EMPTY, SINGLETON);
339    assertFalse(diff.areEqual());
340    assertEquals(EMPTY, diff.entriesOnlyOnLeft());
341    assertEquals(SINGLETON, diff.entriesOnlyOnRight());
342    assertEquals(EMPTY, diff.entriesInCommon());
343    assertEquals(EMPTY, diff.entriesDiffering());
344    assertEquals("not equal: only on right={1=2}", diff.toString());
345  }
346
347  public void testMapDifferenceSingletonEmpty() {
348    MapDifference<Integer, Integer> diff = Maps.difference(SINGLETON, EMPTY);
349    assertFalse(diff.areEqual());
350    assertEquals(SINGLETON, diff.entriesOnlyOnLeft());
351    assertEquals(EMPTY, diff.entriesOnlyOnRight());
352    assertEquals(EMPTY, diff.entriesInCommon());
353    assertEquals(EMPTY, diff.entriesDiffering());
354    assertEquals("not equal: only on left={1=2}", diff.toString());
355  }
356
357  public void testMapDifferenceTypical() {
358    Map<Integer, String> left = ImmutableMap.of(1, "a", 2, "b", 3, "c", 4, "d", 5, "e");
359    Map<Integer, String> right = ImmutableMap.of(1, "a", 3, "f", 5, "g", 6, "z");
360
361    MapDifference<Integer, String> diff1 = Maps.difference(left, right);
362    assertFalse(diff1.areEqual());
363    assertEquals(ImmutableMap.of(2, "b", 4, "d"), diff1.entriesOnlyOnLeft());
364    assertEquals(ImmutableMap.of(6, "z"), diff1.entriesOnlyOnRight());
365    assertEquals(ImmutableMap.of(1, "a"), diff1.entriesInCommon());
366    assertEquals(
367        ImmutableMap.of(3, ValueDifferenceImpl.create("c", "f"), 5,
368            ValueDifferenceImpl.create("e", "g")), diff1.entriesDiffering());
369    assertEquals("not equal: only on left={2=b, 4=d}: only on right={6=z}: "
370        + "value differences={3=(c, f), 5=(e, g)}", diff1.toString());
371
372    MapDifference<Integer, String> diff2 = Maps.difference(right, left);
373    assertFalse(diff2.areEqual());
374    assertEquals(ImmutableMap.of(6, "z"), diff2.entriesOnlyOnLeft());
375    assertEquals(ImmutableMap.of(2, "b", 4, "d"), diff2.entriesOnlyOnRight());
376    assertEquals(ImmutableMap.of(1, "a"), diff2.entriesInCommon());
377    assertEquals(
378        ImmutableMap.of(3, ValueDifferenceImpl.create("f", "c"), 5,
379            ValueDifferenceImpl.create("g", "e")), diff2.entriesDiffering());
380    assertEquals("not equal: only on left={6=z}: only on right={2=b, 4=d}: "
381        + "value differences={3=(f, c), 5=(g, e)}", diff2.toString());
382  }
383
384  public void testMapDifferenceEquals() {
385    Map<Integer, String> left = ImmutableMap.of(1, "a", 2, "b", 3, "c", 4, "d", 5, "e");
386    Map<Integer, String> right = ImmutableMap.of(1, "a", 3, "f", 5, "g", 6, "z");
387    Map<Integer, String> right2 = ImmutableMap.of(1, "a", 3, "h", 5, "g", 6, "z");
388    MapDifference<Integer, String> original = Maps.difference(left, right);
389    MapDifference<Integer, String> same = Maps.difference(left, right);
390    MapDifference<Integer, String> reverse = Maps.difference(right, left);
391    MapDifference<Integer, String> diff2 = Maps.difference(left, right2);
392
393    new EqualsTester().addEqualityGroup(original, same).addEqualityGroup(reverse)
394        .addEqualityGroup(diff2).testEquals();
395  }
396
397  public void testMapDifferencePredicateTypical() {
398    Map<Integer, String> left = ImmutableMap.of(1, "a", 2, "b", 3, "c", 4, "d", 5, "e");
399    Map<Integer, String> right = ImmutableMap.of(1, "A", 3, "F", 5, "G", 6, "Z");
400
401    // TODO(kevinb): replace with Ascii.caseInsensitiveEquivalence() when it
402    // exists
403    Equivalence<String> caseInsensitiveEquivalence = Equivalence.equals().onResultOf(
404        new Function<String, String>() {
405          @Override
406          public String apply(String input) {
407            return input.toLowerCase();
408          }
409        });
410
411    MapDifference<Integer, String> diff1 = Maps.difference(left, right, caseInsensitiveEquivalence);
412    assertFalse(diff1.areEqual());
413    assertEquals(ImmutableMap.of(2, "b", 4, "d"), diff1.entriesOnlyOnLeft());
414    assertEquals(ImmutableMap.of(6, "Z"), diff1.entriesOnlyOnRight());
415    assertEquals(ImmutableMap.of(1, "a"), diff1.entriesInCommon());
416    assertEquals(
417        ImmutableMap.of(3, ValueDifferenceImpl.create("c", "F"), 5,
418            ValueDifferenceImpl.create("e", "G")), diff1.entriesDiffering());
419    assertEquals("not equal: only on left={2=b, 4=d}: only on right={6=Z}: "
420        + "value differences={3=(c, F), 5=(e, G)}", diff1.toString());
421
422    MapDifference<Integer, String> diff2 = Maps.difference(right, left, caseInsensitiveEquivalence);
423    assertFalse(diff2.areEqual());
424    assertEquals(ImmutableMap.of(6, "Z"), diff2.entriesOnlyOnLeft());
425    assertEquals(ImmutableMap.of(2, "b", 4, "d"), diff2.entriesOnlyOnRight());
426    assertEquals(ImmutableMap.of(1, "A"), diff2.entriesInCommon());
427    assertEquals(
428        ImmutableMap.of(3, ValueDifferenceImpl.create("F", "c"), 5,
429            ValueDifferenceImpl.create("G", "e")), diff2.entriesDiffering());
430    assertEquals("not equal: only on left={6=Z}: only on right={2=b, 4=d}: "
431        + "value differences={3=(F, c), 5=(G, e)}", diff2.toString());
432  }
433
434  private static final SortedMap<Integer, Integer> SORTED_EMPTY = Maps.newTreeMap();
435  private static final SortedMap<Integer, Integer> SORTED_SINGLETON = ImmutableSortedMap.of(1, 2);
436
437  public void testMapDifferenceOfSortedMapIsSorted() {
438    Map<Integer, Integer> map = SORTED_SINGLETON;
439    MapDifference<Integer, Integer> difference = Maps.difference(map, EMPTY);
440    assertTrue(difference instanceof SortedMapDifference);
441  }
442
443  public void testSortedMapDifferenceEmptyEmpty() {
444    SortedMapDifference<Integer, Integer> diff = Maps.difference(SORTED_EMPTY, SORTED_EMPTY);
445    assertTrue(diff.areEqual());
446    assertEquals(SORTED_EMPTY, diff.entriesOnlyOnLeft());
447    assertEquals(SORTED_EMPTY, diff.entriesOnlyOnRight());
448    assertEquals(SORTED_EMPTY, diff.entriesInCommon());
449    assertEquals(SORTED_EMPTY, diff.entriesDiffering());
450    assertEquals("equal", diff.toString());
451  }
452
453  public void testSortedMapDifferenceEmptySingleton() {
454    SortedMapDifference<Integer, Integer> diff = Maps.difference(SORTED_EMPTY, SORTED_SINGLETON);
455    assertFalse(diff.areEqual());
456    assertEquals(SORTED_EMPTY, diff.entriesOnlyOnLeft());
457    assertEquals(SORTED_SINGLETON, diff.entriesOnlyOnRight());
458    assertEquals(SORTED_EMPTY, diff.entriesInCommon());
459    assertEquals(SORTED_EMPTY, diff.entriesDiffering());
460    assertEquals("not equal: only on right={1=2}", diff.toString());
461  }
462
463  public void testSortedMapDifferenceSingletonEmpty() {
464    SortedMapDifference<Integer, Integer> diff = Maps.difference(SORTED_SINGLETON, SORTED_EMPTY);
465    assertFalse(diff.areEqual());
466    assertEquals(SORTED_SINGLETON, diff.entriesOnlyOnLeft());
467    assertEquals(SORTED_EMPTY, diff.entriesOnlyOnRight());
468    assertEquals(SORTED_EMPTY, diff.entriesInCommon());
469    assertEquals(SORTED_EMPTY, diff.entriesDiffering());
470    assertEquals("not equal: only on left={1=2}", diff.toString());
471  }
472
473  public void testSortedMapDifferenceTypical() {
474    SortedMap<Integer, String> left = ImmutableSortedMap.<Integer, String>reverseOrder()
475        .put(1, "a").put(2, "b").put(3, "c").put(4, "d").put(5, "e").build();
476
477    SortedMap<Integer, String> right = ImmutableSortedMap.of(1, "a", 3, "f", 5, "g", 6, "z");
478
479    SortedMapDifference<Integer, String> diff1 = Maps.difference(left, right);
480    assertFalse(diff1.areEqual());
481    ASSERT.that(diff1.entriesOnlyOnLeft().entrySet()).has()
482        .allOf(Maps.immutableEntry(4, "d"), Maps.immutableEntry(2, "b")).inOrder();
483    ASSERT.that(diff1.entriesOnlyOnRight().entrySet()).has().item(Maps.immutableEntry(6, "z"));
484    ASSERT.that(diff1.entriesInCommon().entrySet()).has().item(Maps.immutableEntry(1, "a"));
485    ASSERT.that(diff1.entriesDiffering().entrySet())
486        .has()
487        .allOf(Maps.immutableEntry(5, ValueDifferenceImpl.create("e", "g")),
488            Maps.immutableEntry(3, ValueDifferenceImpl.create("c", "f"))).inOrder();
489    assertEquals("not equal: only on left={4=d, 2=b}: only on right={6=z}: "
490        + "value differences={5=(e, g), 3=(c, f)}", diff1.toString());
491
492    SortedMapDifference<Integer, String> diff2 = Maps.difference(right, left);
493    assertFalse(diff2.areEqual());
494    ASSERT.that(diff2.entriesOnlyOnLeft().entrySet()).has().item(Maps.immutableEntry(6, "z"));
495    ASSERT.that(diff2.entriesOnlyOnRight().entrySet()).has()
496        .allOf(Maps.immutableEntry(2, "b"), Maps.immutableEntry(4, "d")).inOrder();
497    ASSERT.that(diff1.entriesInCommon().entrySet()).has().item(Maps.immutableEntry(1, "a"));
498    assertEquals(
499        ImmutableMap.of(3, ValueDifferenceImpl.create("f", "c"), 5,
500            ValueDifferenceImpl.create("g", "e")), diff2.entriesDiffering());
501    assertEquals("not equal: only on left={6=z}: only on right={2=b, 4=d}: "
502        + "value differences={3=(f, c), 5=(g, e)}", diff2.toString());
503  }
504
505  public void testSortedMapDifferenceImmutable() {
506    SortedMap<Integer, String> left = Maps.newTreeMap(ImmutableSortedMap.of(1, "a", 2, "b", 3, "c",
507        4, "d", 5, "e"));
508    SortedMap<Integer, String> right = Maps.newTreeMap(ImmutableSortedMap.of(1, "a", 3, "f", 5,
509        "g", 6, "z"));
510
511    SortedMapDifference<Integer, String> diff1 = Maps.difference(left, right);
512    left.put(6, "z");
513    assertFalse(diff1.areEqual());
514    ASSERT.that(diff1.entriesOnlyOnLeft().entrySet()).has()
515        .allOf(Maps.immutableEntry(2, "b"), Maps.immutableEntry(4, "d")).inOrder();
516    ASSERT.that(diff1.entriesOnlyOnRight().entrySet()).has().item(Maps.immutableEntry(6, "z"));
517    ASSERT.that(diff1.entriesInCommon().entrySet()).has().item(Maps.immutableEntry(1, "a"));
518    ASSERT.that(diff1.entriesDiffering().entrySet())
519        .has()
520        .allOf(Maps.immutableEntry(3, ValueDifferenceImpl.create("c", "f")),
521            Maps.immutableEntry(5, ValueDifferenceImpl.create("e", "g"))).inOrder();
522    try {
523      diff1.entriesInCommon().put(7, "x");
524      fail();
525    } catch (UnsupportedOperationException expected) {}
526    try {
527      diff1.entriesOnlyOnLeft().put(7, "x");
528      fail();
529    } catch (UnsupportedOperationException expected) {}
530    try {
531      diff1.entriesOnlyOnRight().put(7, "x");
532      fail();
533    } catch (UnsupportedOperationException expected) {}
534  }
535
536  public void testSortedMapDifferenceEquals() {
537    SortedMap<Integer, String> left = ImmutableSortedMap.of(1, "a", 2, "b", 3, "c", 4, "d", 5, "e");
538    SortedMap<Integer, String> right = ImmutableSortedMap.of(1, "a", 3, "f", 5, "g", 6, "z");
539    SortedMap<Integer, String> right2 = ImmutableSortedMap.of(1, "a", 3, "h", 5, "g", 6, "z");
540    SortedMapDifference<Integer, String> original = Maps.difference(left, right);
541    SortedMapDifference<Integer, String> same = Maps.difference(left, right);
542    SortedMapDifference<Integer, String> reverse = Maps.difference(right, left);
543    SortedMapDifference<Integer, String> diff2 = Maps.difference(left, right2);
544
545    new EqualsTester().addEqualityGroup(original, same).addEqualityGroup(reverse)
546        .addEqualityGroup(diff2).testEquals();
547  }
548
549  private static final Function<String, Integer> LENGTH_FUNCTION = new Function<String, Integer>() {
550    @Override
551    public Integer apply(String input) {
552      return input.length();
553    }
554  };
555
556  public void testAsMap() {
557    Set<String> strings = ImmutableSet.of("one", "two", "three");
558    Map<String, Integer> map = Maps.asMap(strings, LENGTH_FUNCTION);
559    assertEquals(ImmutableMap.of("one", 3, "two", 3, "three", 5), map);
560    assertEquals(Integer.valueOf(5), map.get("three"));
561    assertNull(map.get("five"));
562    ASSERT.that(map.entrySet()).has()
563        .allOf(mapEntry("one", 3), mapEntry("two", 3), mapEntry("three", 5)).inOrder();
564  }
565
566  public void testAsMapReadsThrough() {
567    Set<String> strings = Sets.newLinkedHashSet();
568    Collections.addAll(strings, "one", "two", "three");
569    Map<String, Integer> map = Maps.asMap(strings, LENGTH_FUNCTION);
570    assertEquals(ImmutableMap.of("one", 3, "two", 3, "three", 5), map);
571    assertNull(map.get("four"));
572    strings.add("four");
573    assertEquals(ImmutableMap.of("one", 3, "two", 3, "three", 5, "four", 4), map);
574    assertEquals(Integer.valueOf(4), map.get("four"));
575  }
576
577  public void testAsMapWritesThrough() {
578    Set<String> strings = Sets.newLinkedHashSet();
579    Collections.addAll(strings, "one", "two", "three");
580    Map<String, Integer> map = Maps.asMap(strings, LENGTH_FUNCTION);
581    assertEquals(ImmutableMap.of("one", 3, "two", 3, "three", 5), map);
582    assertEquals(Integer.valueOf(3), map.remove("two"));
583    ASSERT.that(strings).has().allOf("one", "three").inOrder();
584  }
585
586  public void testAsMapEmpty() {
587    Set<String> strings = ImmutableSet.of();
588    Map<String, Integer> map = Maps.asMap(strings, LENGTH_FUNCTION);
589    ASSERT.that(map.entrySet()).isEmpty();
590    assertTrue(map.isEmpty());
591    assertNull(map.get("five"));
592  }
593
594  private static class NonNavigableSortedSet extends ForwardingSortedSet<String> {
595    private final SortedSet<String> delegate = Sets.newTreeSet();
596
597    @Override
598    protected SortedSet<String> delegate() {
599      return delegate;
600    }
601  }
602
603  public void testAsMapReturnsSortedMapForSortedSetInput() {
604    Set<String> set = new NonNavigableSortedSet();
605    assertTrue(Maps.asMap(set, Functions.identity()) instanceof SortedMap);
606  }
607
608  public void testAsMapSorted() {
609    SortedSet<String> strings = new NonNavigableSortedSet();
610    Collections.addAll(strings, "one", "two", "three");
611    SortedMap<String, Integer> map = Maps.asMap(strings, LENGTH_FUNCTION);
612    assertEquals(ImmutableMap.of("one", 3, "two", 3, "three", 5), map);
613    assertEquals(Integer.valueOf(5), map.get("three"));
614    assertNull(map.get("five"));
615    ASSERT.that(map.entrySet()).has()
616        .allOf(mapEntry("one", 3), mapEntry("three", 5), mapEntry("two", 3)).inOrder();
617    ASSERT.that(map.tailMap("onea").entrySet()).has()
618        .allOf(mapEntry("three", 5), mapEntry("two", 3)).inOrder();
619    ASSERT.that(map.subMap("one", "two").entrySet()).has()
620        .allOf(mapEntry("one", 3), mapEntry("three", 5)).inOrder();
621  }
622
623  public void testAsMapSortedReadsThrough() {
624    SortedSet<String> strings = new NonNavigableSortedSet();
625    Collections.addAll(strings, "one", "two", "three");
626    SortedMap<String, Integer> map = Maps.asMap(strings, LENGTH_FUNCTION);
627    assertNull(map.comparator());
628    assertEquals(ImmutableSortedMap.of("one", 3, "two", 3, "three", 5), map);
629    assertNull(map.get("four"));
630    strings.add("four");
631    assertEquals(ImmutableSortedMap.of("one", 3, "two", 3, "three", 5, "four", 4), map);
632    assertEquals(Integer.valueOf(4), map.get("four"));
633    SortedMap<String, Integer> headMap = map.headMap("two");
634    assertEquals(ImmutableSortedMap.of("four", 4, "one", 3, "three", 5), headMap);
635    strings.add("five");
636    strings.remove("one");
637    assertEquals(ImmutableSortedMap.of("five", 4, "four", 4, "three", 5), headMap);
638    ASSERT.that(map.entrySet()).has()
639        .allOf(mapEntry("five", 4), mapEntry("four", 4), mapEntry("three", 5), mapEntry("two", 3))
640        .inOrder();
641  }
642
643  public void testAsMapSortedWritesThrough() {
644    SortedSet<String> strings = new NonNavigableSortedSet();
645    Collections.addAll(strings, "one", "two", "three");
646    SortedMap<String, Integer> map = Maps.asMap(strings, LENGTH_FUNCTION);
647    assertEquals(ImmutableMap.of("one", 3, "two", 3, "three", 5), map);
648    assertEquals(Integer.valueOf(3), map.remove("two"));
649    ASSERT.that(strings).has().allOf("one", "three").inOrder();
650  }
651
652  public void testAsMapSortedSubViewKeySetsDoNotSupportAdd() {
653    SortedMap<String, Integer> map = Maps.asMap(new NonNavigableSortedSet(), LENGTH_FUNCTION);
654    try {
655      map.subMap("a", "z").keySet().add("a");
656      fail();
657    } catch (UnsupportedOperationException expected) {}
658    try {
659      map.tailMap("a").keySet().add("a");
660      fail();
661    } catch (UnsupportedOperationException expected) {}
662    try {
663      map.headMap("r").keySet().add("a");
664      fail();
665    } catch (UnsupportedOperationException expected) {}
666    try {
667      map.headMap("r").tailMap("m").keySet().add("a");
668      fail();
669    } catch (UnsupportedOperationException expected) {}
670  }
671
672  public void testAsMapSortedEmpty() {
673    SortedSet<String> strings = new NonNavigableSortedSet();
674    SortedMap<String, Integer> map = Maps.asMap(strings, LENGTH_FUNCTION);
675    ASSERT.that(map.entrySet()).isEmpty();
676    assertTrue(map.isEmpty());
677    assertNull(map.get("five"));
678  }
679
680  public void testToMap() {
681    Iterable<String> strings = ImmutableList.of("one", "two", "three");
682    ImmutableMap<String, Integer> map = Maps.toMap(strings, LENGTH_FUNCTION);
683    assertEquals(ImmutableMap.of("one", 3, "two", 3, "three", 5), map);
684    ASSERT.that(map.entrySet()).has()
685        .allOf(mapEntry("one", 3), mapEntry("two", 3), mapEntry("three", 5)).inOrder();
686  }
687
688  public void testToMapIterator() {
689    Iterator<String> strings = ImmutableList.of("one", "two", "three").iterator();
690    ImmutableMap<String, Integer> map = Maps.toMap(strings, LENGTH_FUNCTION);
691    assertEquals(ImmutableMap.of("one", 3, "two", 3, "three", 5), map);
692    ASSERT.that(map.entrySet()).has()
693        .allOf(mapEntry("one", 3), mapEntry("two", 3), mapEntry("three", 5)).inOrder();
694  }
695
696  public void testToMapWithDuplicateKeys() {
697    Iterable<String> strings = ImmutableList.of("one", "two", "three", "two", "one");
698    ImmutableMap<String, Integer> map = Maps.toMap(strings, LENGTH_FUNCTION);
699    assertEquals(ImmutableMap.of("one", 3, "two", 3, "three", 5), map);
700    ASSERT.that(map.entrySet()).has()
701        .allOf(mapEntry("one", 3), mapEntry("two", 3), mapEntry("three", 5)).inOrder();
702  }
703
704  public void testToMapWithNullKeys() {
705    Iterable<String> strings = Arrays.asList("one", null, "three");
706    try {
707      Maps.toMap(strings, Functions.constant("foo"));
708      fail();
709    } catch (NullPointerException expected) {}
710  }
711
712  public void testToMapWithNullValues() {
713    Iterable<String> strings = ImmutableList.of("one", "two", "three");
714    try {
715      Maps.toMap(strings, Functions.constant(null));
716      fail();
717    } catch (NullPointerException expected) {}
718  }
719
720  private static final BiMap<Integer, String> INT_TO_STRING_MAP = new ImmutableBiMap.Builder<Integer, String>()
721      .put(1, "one").put(2, "two").put(3, "three").build();
722
723  public void testUniqueIndexCollection() {
724    ImmutableMap<Integer, String> outputMap = Maps.uniqueIndex(INT_TO_STRING_MAP.values(),
725        Functions.forMap(INT_TO_STRING_MAP.inverse()));
726    assertEquals(INT_TO_STRING_MAP, outputMap);
727  }
728
729  public void testUniqueIndexIterable() {
730    ImmutableMap<Integer, String> outputMap = Maps.uniqueIndex(new Iterable<String>() {
731      @Override
732      public Iterator<String> iterator() {
733        return INT_TO_STRING_MAP.values().iterator();
734      }
735    }, Functions.forMap(INT_TO_STRING_MAP.inverse()));
736    assertEquals(INT_TO_STRING_MAP, outputMap);
737  }
738
739  public void testUniqueIndexIterator() {
740    ImmutableMap<Integer, String> outputMap = Maps.uniqueIndex(INT_TO_STRING_MAP.values()
741        .iterator(), Functions.forMap(INT_TO_STRING_MAP.inverse()));
742    assertEquals(INT_TO_STRING_MAP, outputMap);
743  }
744
745  /** Can't create the map if more than one value maps to the same key. */
746  public void testUniqueIndexDuplicates() {
747    try {
748      Maps.uniqueIndex(ImmutableSet.of("one", "uno"), Functions.constant(1));
749      fail();
750    } catch (IllegalArgumentException expected) {}
751  }
752
753  /** Null values are not allowed. */
754  public void testUniqueIndexNullValue() {
755    List<String> listWithNull = Lists.newArrayList((String) null);
756    try {
757      Maps.uniqueIndex(listWithNull, Functions.constant(1));
758      fail();
759    } catch (NullPointerException expected) {}
760  }
761
762  /** Null keys aren't allowed either. */
763  public void testUniqueIndexNullKey() {
764    List<String> oneStringList = Lists.newArrayList("foo");
765    try {
766      Maps.uniqueIndex(oneStringList, Functions.constant(null));
767      fail();
768    } catch (NullPointerException expected) {}
769  }
770
771  @GwtIncompatible("Maps.fromProperties")
772  @SuppressWarnings("deprecation")
773  // StringBufferInputStream
774  public void testFromProperties() throws IOException {
775    Properties testProp = new Properties();
776
777    Map<String, String> result = Maps.fromProperties(testProp);
778    assertTrue(result.isEmpty());
779    testProp.setProperty("first", "true");
780
781    result = Maps.fromProperties(testProp);
782    assertEquals("true", result.get("first"));
783    assertEquals(1, result.size());
784    testProp.setProperty("second", "null");
785
786    result = Maps.fromProperties(testProp);
787    assertEquals("true", result.get("first"));
788    assertEquals("null", result.get("second"));
789    assertEquals(2, result.size());
790
791    // Now test values loaded from a stream.
792    String props = "test\n second = 2\n Third item :   a short  phrase   ";
793
794    testProp.load(new StringBufferInputStream(props));
795
796    result = Maps.fromProperties(testProp);
797    assertEquals(4, result.size());
798    assertEquals("true", result.get("first"));
799    assertEquals("", result.get("test"));
800    assertEquals("2", result.get("second"));
801    assertEquals("item :   a short  phrase   ", result.get("Third"));
802    assertFalse(result.containsKey("not here"));
803
804    // Test loading system properties
805    result = Maps.fromProperties(System.getProperties());
806    assertTrue(result.containsKey("java.version"));
807
808    // Test that defaults work, too.
809    testProp = new Properties(System.getProperties());
810    String override = "test\njava.version : hidden";
811
812    testProp.load(new StringBufferInputStream(override));
813
814    result = Maps.fromProperties(testProp);
815    assertTrue(result.size() > 2);
816    assertEquals("", result.get("test"));
817    assertEquals("hidden", result.get("java.version"));
818    assertNotSame(System.getProperty("java.version"), result.get("java.version"));
819  }
820
821  @GwtIncompatible("Maps.fromProperties")
822  @SuppressWarnings("serial")
823  // never serialized
824  public void testFromPropertiesNullKey() {
825    Properties properties = new Properties() {
826      @Override
827      public Enumeration<?> propertyNames() {
828        return Iterators.asEnumeration(Arrays.asList(null, "first", "second").iterator());
829      }
830    };
831    properties.setProperty("first", "true");
832    properties.setProperty("second", "null");
833
834    try {
835      Maps.fromProperties(properties);
836      fail();
837    } catch (NullPointerException expected) {}
838  }
839
840  @GwtIncompatible("Maps.fromProperties")
841  @SuppressWarnings("serial")
842  // never serialized
843  public void testFromPropertiesNonStringKeys() {
844    Properties properties = new Properties() {
845      @Override
846      public Enumeration<?> propertyNames() {
847        return Iterators.asEnumeration(Arrays.<Object>asList(Integer.valueOf(123), "first")
848            .iterator());
849      }
850    };
851
852    try {
853      Maps.fromProperties(properties);
854      fail();
855    } catch (ClassCastException expected) {}
856  }
857
858  /**
859   * Constructs a "nefarious" map entry with the specified key and value,
860   * meaning an entry that is suitable for testing that map entries cannot be
861   * modified via a nefarious implementation of equals. This is used for testing
862   * unmodifiable collections of map entries; for example, it should not be
863   * possible to access the raw (modifiable) map entry via a nefarious equals
864   * method.
865   */
866  public static <K, V> Map.Entry<K, V> nefariousEntry(final K key, final V value) {
867    return new AbstractMapEntry<K, V>() {
868      @Override
869      public K getKey() {
870        return key;
871      }
872
873      @Override
874      public V getValue() {
875        return value;
876      }
877
878      @Override
879      public V setValue(V value) {
880        throw new UnsupportedOperationException();
881      }
882
883      @SuppressWarnings("unchecked")
884      @Override
885      public boolean equals(Object o) {
886        if (o instanceof Map.Entry) {
887          Map.Entry<K, V> e = (Map.Entry<K, V>) o;
888          e.setValue(value); // muhahaha!
889        }
890        return super.equals(o);
891      }
892    };
893  }
894
895  public void testUnmodifiableBiMap() {
896    BiMap<Integer, String> mod = HashBiMap.create();
897    mod.put(1, "one");
898    mod.put(2, "two");
899    mod.put(3, "three");
900
901    BiMap<Number, String> unmod = Maps.<Number, String>unmodifiableBiMap(mod);
902
903    /* No aliasing on inverse operations. */
904    assertSame(unmod.inverse(), unmod.inverse());
905    assertSame(unmod, unmod.inverse().inverse());
906
907    /* Unmodifiable is a view. */
908    mod.put(4, "four");
909    assertEquals(true, unmod.get(4).equals("four"));
910    assertEquals(true, unmod.inverse().get("four").equals(4));
911
912    /* UnsupportedOperationException on direct modifications. */
913    try {
914      unmod.put(4, "four");
915      fail("UnsupportedOperationException expected");
916    } catch (UnsupportedOperationException expected) {}
917    try {
918      unmod.forcePut(4, "four");
919      fail("UnsupportedOperationException expected");
920    } catch (UnsupportedOperationException expected) {}
921    try {
922      unmod.putAll(Collections.singletonMap(4, "four"));
923      fail("UnsupportedOperationException expected");
924    } catch (UnsupportedOperationException expected) {}
925
926    /* UnsupportedOperationException on indirect modifications. */
927    BiMap<String, Number> inverse = unmod.inverse();
928    try {
929      inverse.put("four", 4);
930      fail("UnsupportedOperationException expected");
931    } catch (UnsupportedOperationException expected) {}
932    try {
933      inverse.forcePut("four", 4);
934      fail("UnsupportedOperationException expected");
935    } catch (UnsupportedOperationException expected) {}
936    try {
937      inverse.putAll(Collections.singletonMap("four", 4));
938      fail("UnsupportedOperationException expected");
939    } catch (UnsupportedOperationException expected) {}
940    Set<String> values = unmod.values();
941    try {
942      values.remove("four");
943      fail("UnsupportedOperationException expected");
944    } catch (UnsupportedOperationException expected) {}
945    Set<Map.Entry<Number, String>> entries = unmod.entrySet();
946    Map.Entry<Number, String> entry = entries.iterator().next();
947    try {
948      entry.setValue("four");
949      fail("UnsupportedOperationException expected");
950    } catch (UnsupportedOperationException expected) {}
951    @SuppressWarnings("unchecked")
952    Map.Entry<Integer, String> entry2 = (Map.Entry<Integer, String>) entries.toArray()[0];
953    try {
954      entry2.setValue("four");
955      fail("UnsupportedOperationException expected");
956    } catch (UnsupportedOperationException expected) {}
957  }
958
959  public void testImmutableEntry() {
960    Map.Entry<String, Integer> e = Maps.immutableEntry("foo", 1);
961    assertEquals("foo", e.getKey());
962    assertEquals(1, (int) e.getValue());
963    try {
964      e.setValue(2);
965      fail("UnsupportedOperationException expected");
966    } catch (UnsupportedOperationException expected) {}
967    assertEquals("foo=1", e.toString());
968    assertEquals(101575, e.hashCode());
969  }
970
971  public void testImmutableEntryNull() {
972    Map.Entry<String, Integer> e = Maps.immutableEntry((String) null, (Integer) null);
973    assertNull(e.getKey());
974    assertNull(e.getValue());
975    try {
976      e.setValue(null);
977      fail("UnsupportedOperationException expected");
978    } catch (UnsupportedOperationException expected) {}
979    assertEquals("null=null", e.toString());
980    assertEquals(0, e.hashCode());
981  }
982
983  /** See {@link SynchronizedBiMapTest} for more tests. */
984  public void testSynchronizedBiMap() {
985    BiMap<String, Integer> bimap = HashBiMap.create();
986    bimap.put("one", 1);
987    BiMap<String, Integer> sync = Maps.synchronizedBiMap(bimap);
988    bimap.put("two", 2);
989    sync.put("three", 3);
990    assertEquals(ImmutableSet.of(1, 2, 3), bimap.inverse().keySet());
991    assertEquals(ImmutableSet.of(1, 2, 3), sync.inverse().keySet());
992  }
993
994  private static final Predicate<String> NOT_LENGTH_3 = new Predicate<String>() {
995    @Override
996    public boolean apply(String input) {
997      return input == null || input.length() != 3;
998    }
999  };
1000
1001  private static final Predicate<Integer> EVEN = new Predicate<Integer>() {
1002    @Override
1003    public boolean apply(Integer input) {
1004      return input == null || input % 2 == 0;
1005    }
1006  };
1007
1008  private static final Predicate<Entry<String, Integer>> CORRECT_LENGTH = new Predicate<Entry<String, Integer>>() {
1009    @Override
1010    public boolean apply(Entry<String, Integer> input) {
1011      return input.getKey().length() == input.getValue();
1012    }
1013  };
1014
1015  private static final Function<Integer, Double> SQRT_FUNCTION = new Function<Integer, Double>() {
1016    @Override
1017    public Double apply(Integer in) {
1018      return Math.sqrt(in);
1019    }
1020  };
1021
1022  public static class FilteredMapTest extends TestCase {
1023    Map<String, Integer> createUnfiltered() {
1024      return Maps.newHashMap();
1025    }
1026
1027    public void testFilteredKeysIllegalPut() {
1028      Map<String, Integer> unfiltered = createUnfiltered();
1029      Map<String, Integer> filtered = Maps.filterKeys(unfiltered, NOT_LENGTH_3);
1030      filtered.put("a", 1);
1031      filtered.put("b", 2);
1032      assertEquals(ImmutableMap.of("a", 1, "b", 2), filtered);
1033
1034      try {
1035        filtered.put("yyy", 3);
1036        fail();
1037      } catch (IllegalArgumentException expected) {}
1038    }
1039
1040    public void testFilteredKeysIllegalPutAll() {
1041      Map<String, Integer> unfiltered = createUnfiltered();
1042      Map<String, Integer> filtered = Maps.filterKeys(unfiltered, NOT_LENGTH_3);
1043      filtered.put("a", 1);
1044      filtered.put("b", 2);
1045      assertEquals(ImmutableMap.of("a", 1, "b", 2), filtered);
1046
1047      try {
1048        filtered.putAll(ImmutableMap.of("c", 3, "zzz", 4, "b", 5));
1049        fail();
1050      } catch (IllegalArgumentException expected) {}
1051
1052      assertEquals(ImmutableMap.of("a", 1, "b", 2), filtered);
1053    }
1054
1055    public void testFilteredKeysFilteredReflectsBackingChanges() {
1056      Map<String, Integer> unfiltered = createUnfiltered();
1057      Map<String, Integer> filtered = Maps.filterKeys(unfiltered, NOT_LENGTH_3);
1058      unfiltered.put("two", 2);
1059      unfiltered.put("three", 3);
1060      unfiltered.put("four", 4);
1061      assertEquals(ImmutableMap.of("two", 2, "three", 3, "four", 4), unfiltered);
1062      assertEquals(ImmutableMap.of("three", 3, "four", 4), filtered);
1063
1064      unfiltered.remove("three");
1065      assertEquals(ImmutableMap.of("two", 2, "four", 4), unfiltered);
1066      assertEquals(ImmutableMap.of("four", 4), filtered);
1067
1068      unfiltered.clear();
1069      assertEquals(ImmutableMap.of(), unfiltered);
1070      assertEquals(ImmutableMap.of(), filtered);
1071    }
1072
1073    public void testFilteredValuesIllegalPut() {
1074      Map<String, Integer> unfiltered = createUnfiltered();
1075      Map<String, Integer> filtered = Maps.filterValues(unfiltered, EVEN);
1076      filtered.put("a", 2);
1077      unfiltered.put("b", 4);
1078      unfiltered.put("c", 5);
1079      assertEquals(ImmutableMap.of("a", 2, "b", 4), filtered);
1080
1081      try {
1082        filtered.put("yyy", 3);
1083        fail();
1084      } catch (IllegalArgumentException expected) {}
1085      assertEquals(ImmutableMap.of("a", 2, "b", 4), filtered);
1086    }
1087
1088    public void testFilteredValuesIllegalPutAll() {
1089      Map<String, Integer> unfiltered = createUnfiltered();
1090      Map<String, Integer> filtered = Maps.filterValues(unfiltered, EVEN);
1091      filtered.put("a", 2);
1092      unfiltered.put("b", 4);
1093      unfiltered.put("c", 5);
1094      assertEquals(ImmutableMap.of("a", 2, "b", 4), filtered);
1095
1096      try {
1097        filtered.putAll(ImmutableMap.of("c", 4, "zzz", 5, "b", 6));
1098        fail();
1099      } catch (IllegalArgumentException expected) {}
1100      assertEquals(ImmutableMap.of("a", 2, "b", 4), filtered);
1101    }
1102
1103    public void testFilteredValuesIllegalSetValue() {
1104      Map<String, Integer> unfiltered = createUnfiltered();
1105      Map<String, Integer> filtered = Maps.filterValues(unfiltered, EVEN);
1106      filtered.put("a", 2);
1107      filtered.put("b", 4);
1108      assertEquals(ImmutableMap.of("a", 2, "b", 4), filtered);
1109
1110      Entry<String, Integer> entry = filtered.entrySet().iterator().next();
1111      try {
1112        entry.setValue(5);
1113        fail();
1114      } catch (IllegalArgumentException expected) {}
1115
1116      assertEquals(ImmutableMap.of("a", 2, "b", 4), filtered);
1117    }
1118
1119    public void testFilteredValuesClear() {
1120      Map<String, Integer> unfiltered = createUnfiltered();
1121      unfiltered.put("one", 1);
1122      unfiltered.put("two", 2);
1123      unfiltered.put("three", 3);
1124      unfiltered.put("four", 4);
1125      Map<String, Integer> filtered = Maps.filterValues(unfiltered, EVEN);
1126      assertEquals(ImmutableMap.of("one", 1, "two", 2, "three", 3, "four", 4), unfiltered);
1127      assertEquals(ImmutableMap.of("two", 2, "four", 4), filtered);
1128
1129      filtered.clear();
1130      assertEquals(ImmutableMap.of("one", 1, "three", 3), unfiltered);
1131      assertTrue(filtered.isEmpty());
1132    }
1133
1134    public void testFilteredEntriesIllegalPut() {
1135      Map<String, Integer> unfiltered = createUnfiltered();
1136      unfiltered.put("cat", 3);
1137      unfiltered.put("dog", 2);
1138      unfiltered.put("horse", 5);
1139      Map<String, Integer> filtered = Maps.filterEntries(unfiltered, CORRECT_LENGTH);
1140      assertEquals(ImmutableMap.of("cat", 3, "horse", 5), filtered);
1141
1142      filtered.put("chicken", 7);
1143      assertEquals(ImmutableMap.of("cat", 3, "horse", 5, "chicken", 7), filtered);
1144
1145      try {
1146        filtered.put("cow", 7);
1147        fail();
1148      } catch (IllegalArgumentException expected) {}
1149      assertEquals(ImmutableMap.of("cat", 3, "horse", 5, "chicken", 7), filtered);
1150    }
1151
1152    public void testFilteredEntriesIllegalPutAll() {
1153      Map<String, Integer> unfiltered = createUnfiltered();
1154      unfiltered.put("cat", 3);
1155      unfiltered.put("dog", 2);
1156      unfiltered.put("horse", 5);
1157      Map<String, Integer> filtered = Maps.filterEntries(unfiltered, CORRECT_LENGTH);
1158      assertEquals(ImmutableMap.of("cat", 3, "horse", 5), filtered);
1159
1160      filtered.put("chicken", 7);
1161      assertEquals(ImmutableMap.of("cat", 3, "horse", 5, "chicken", 7), filtered);
1162
1163      try {
1164        filtered.putAll(ImmutableMap.of("sheep", 5, "cow", 7));
1165        fail();
1166      } catch (IllegalArgumentException expected) {}
1167      assertEquals(ImmutableMap.of("cat", 3, "horse", 5, "chicken", 7), filtered);
1168    }
1169
1170    public void testFilteredEntriesObjectPredicate() {
1171      Map<String, Integer> unfiltered = createUnfiltered();
1172      unfiltered.put("cat", 3);
1173      unfiltered.put("dog", 2);
1174      unfiltered.put("horse", 5);
1175      Predicate<Object> predicate = Predicates.alwaysFalse();
1176      Map<String, Integer> filtered = Maps.filterEntries(unfiltered, predicate);
1177      assertTrue(filtered.isEmpty());
1178    }
1179
1180    public void testFilteredEntriesWildCardEntryPredicate() {
1181      Map<String, Integer> unfiltered = createUnfiltered();
1182      unfiltered.put("cat", 3);
1183      unfiltered.put("dog", 2);
1184      unfiltered.put("horse", 5);
1185      Predicate<Entry<?, ?>> predicate = new Predicate<Entry<?, ?>>() {
1186        @Override
1187        public boolean apply(Entry<?, ?> input) {
1188          return "cat".equals(input.getKey()) || Integer.valueOf(2) == input.getValue();
1189        }
1190      };
1191      Map<String, Integer> filtered = Maps.filterEntries(unfiltered, predicate);
1192      assertEquals(ImmutableMap.of("cat", 3, "dog", 2), filtered);
1193    }
1194  }
1195
1196  public static class FilteredSortedMapTest extends FilteredMapTest {
1197    @Override
1198    SortedMap<String, Integer> createUnfiltered() {
1199      return Maps.newTreeMap();
1200    }
1201
1202    public void testFilterKeysIdentifiesSortedMap() {
1203      SortedMap<String, Integer> map = createUnfiltered();
1204      assertTrue(Maps.filterKeys((Map<String, Integer>) map, NOT_LENGTH_3) instanceof SortedMap);
1205    }
1206
1207    public void testFilterValuesIdentifiesSortedMap() {
1208      SortedMap<String, Integer> map = createUnfiltered();
1209      assertTrue(Maps.filterValues((Map<String, Integer>) map, EVEN) instanceof SortedMap);
1210    }
1211
1212    public void testFilterEntriesIdentifiesSortedMap() {
1213      SortedMap<String, Integer> map = createUnfiltered();
1214      assertTrue(Maps.filterEntries((Map<String, Integer>) map, CORRECT_LENGTH) instanceof SortedMap);
1215    }
1216
1217    public void testFirstAndLastKeyFilteredMap() {
1218      SortedMap<String, Integer> unfiltered = createUnfiltered();
1219      unfiltered.put("apple", 2);
1220      unfiltered.put("banana", 6);
1221      unfiltered.put("cat", 3);
1222      unfiltered.put("dog", 5);
1223
1224      SortedMap<String, Integer> filtered = Maps.filterEntries(unfiltered, CORRECT_LENGTH);
1225      assertEquals("banana", filtered.firstKey());
1226      assertEquals("cat", filtered.lastKey());
1227    }
1228
1229    public void testHeadSubTailMap_FilteredMap() {
1230      SortedMap<String, Integer> unfiltered = createUnfiltered();
1231      unfiltered.put("apple", 2);
1232      unfiltered.put("banana", 6);
1233      unfiltered.put("cat", 4);
1234      unfiltered.put("dog", 3);
1235      SortedMap<String, Integer> filtered = Maps.filterEntries(unfiltered, CORRECT_LENGTH);
1236
1237      assertEquals(ImmutableMap.of("banana", 6), filtered.headMap("dog"));
1238      assertEquals(ImmutableMap.of(), filtered.headMap("banana"));
1239      assertEquals(ImmutableMap.of("banana", 6, "dog", 3), filtered.headMap("emu"));
1240
1241      assertEquals(ImmutableMap.of("banana", 6), filtered.subMap("banana", "dog"));
1242      assertEquals(ImmutableMap.of("dog", 3), filtered.subMap("cat", "emu"));
1243
1244      assertEquals(ImmutableMap.of("dog", 3), filtered.tailMap("cat"));
1245      assertEquals(ImmutableMap.of("banana", 6, "dog", 3), filtered.tailMap("banana"));
1246    }
1247  }
1248
1249  public static class FilteredBiMapTest extends FilteredMapTest {
1250    @Override
1251    BiMap<String, Integer> createUnfiltered() {
1252      return HashBiMap.create();
1253    }
1254
1255    public void testFilterKeysIdentifiesBiMap() {
1256      BiMap<String, Integer> map = createUnfiltered();
1257      assertTrue(Maps.filterKeys((Map<String, Integer>) map, NOT_LENGTH_3) instanceof BiMap);
1258    }
1259
1260    public void testFilterValuesIdentifiesBiMap() {
1261      BiMap<String, Integer> map = createUnfiltered();
1262      assertTrue(Maps.filterValues((Map<String, Integer>) map, EVEN) instanceof BiMap);
1263    }
1264
1265    public void testFilterEntriesIdentifiesBiMap() {
1266      BiMap<String, Integer> map = createUnfiltered();
1267      assertTrue(Maps.filterEntries((Map<String, Integer>) map, CORRECT_LENGTH) instanceof BiMap);
1268    }
1269  }
1270
1271  public void testTransformValues() {
1272    Map<String, Integer> map = ImmutableMap.of("a", 4, "b", 9);
1273    Map<String, Double> transformed = transformValues(map, SQRT_FUNCTION);
1274
1275    assertEquals(ImmutableMap.of("a", 2.0, "b", 3.0), transformed);
1276  }
1277
1278  public void testTransformValuesSecretlySorted() {
1279    Map<String, Integer> map = sortedNotNavigable(ImmutableSortedMap.of("a", 4, "b", 9));
1280    Map<String, Double> transformed = transformValues(map, SQRT_FUNCTION);
1281
1282    assertEquals(ImmutableMap.of("a", 2.0, "b", 3.0), transformed);
1283    assertTrue(transformed instanceof SortedMap);
1284  }
1285
1286  public void testTransformEntries() {
1287    Map<String, String> map = ImmutableMap.of("a", "4", "b", "9");
1288    EntryTransformer<String, String, String> concat = new EntryTransformer<String, String, String>() {
1289      @Override
1290      public String transformEntry(String key, String value) {
1291        return key + value;
1292      }
1293    };
1294    Map<String, String> transformed = transformEntries(map, concat);
1295
1296    assertEquals(ImmutableMap.of("a", "a4", "b", "b9"), transformed);
1297  }
1298
1299  public void testTransformEntriesSecretlySorted() {
1300    Map<String, String> map = ImmutableSortedMap.of("a", "4", "b", "9");
1301    EntryTransformer<String, String, String> concat = new EntryTransformer<String, String, String>() {
1302      @Override
1303      public String transformEntry(String key, String value) {
1304        return key + value;
1305      }
1306    };
1307    Map<String, String> transformed = transformEntries(map, concat);
1308
1309    assertEquals(ImmutableMap.of("a", "a4", "b", "b9"), transformed);
1310    assertTrue(transformed instanceof SortedMap);
1311  }
1312
1313  public void testTransformEntriesGenerics() {
1314    Map<Object, Object> map1 = ImmutableMap.<Object, Object>of(1, 2);
1315    Map<Object, Number> map2 = ImmutableMap.<Object, Number>of(1, 2);
1316    Map<Object, Integer> map3 = ImmutableMap.<Object, Integer>of(1, 2);
1317    Map<Number, Object> map4 = ImmutableMap.<Number, Object>of(1, 2);
1318    Map<Number, Number> map5 = ImmutableMap.<Number, Number>of(1, 2);
1319    Map<Number, Integer> map6 = ImmutableMap.<Number, Integer>of(1, 2);
1320    Map<Integer, Object> map7 = ImmutableMap.<Integer, Object>of(1, 2);
1321    Map<Integer, Number> map8 = ImmutableMap.<Integer, Number>of(1, 2);
1322    Map<Integer, Integer> map9 = ImmutableMap.<Integer, Integer>of(1, 2);
1323    Map<? extends Number, ? extends Number> map0 = ImmutableMap.of(1, 2);
1324
1325    EntryTransformer<Number, Number, Double> transformer = new EntryTransformer<Number, Number, Double>() {
1326      @Override
1327      public Double transformEntry(Number key, Number value) {
1328        return key.doubleValue() + value.doubleValue();
1329      }
1330    };
1331
1332    Map<Object, Double> objectKeyed;
1333    Map<Number, Double> numberKeyed;
1334    Map<Integer, Double> integerKeyed;
1335
1336    numberKeyed = transformEntries(map5, transformer);
1337    numberKeyed = transformEntries(map6, transformer);
1338    integerKeyed = transformEntries(map8, transformer);
1339    integerKeyed = transformEntries(map9, transformer);
1340
1341    Map<? extends Number, Double> wildcarded = transformEntries(map0, transformer);
1342
1343    // Can't loosen the key type:
1344    // objectKeyed = transformEntries(map5, transformer);
1345    // objectKeyed = transformEntries(map6, transformer);
1346    // objectKeyed = transformEntries(map8, transformer);
1347    // objectKeyed = transformEntries(map9, transformer);
1348    // numberKeyed = transformEntries(map8, transformer);
1349    // numberKeyed = transformEntries(map9, transformer);
1350
1351    // Can't loosen the value type:
1352    // Map<Number, Number> looseValued1 = transformEntries(map5, transformer);
1353    // Map<Number, Number> looseValued2 = transformEntries(map6, transformer);
1354    // Map<Integer, Number> looseValued3 = transformEntries(map8, transformer);
1355    // Map<Integer, Number> looseValued4 = transformEntries(map9, transformer);
1356
1357    // Can't call with too loose a key:
1358    // transformEntries(map1, transformer);
1359    // transformEntries(map2, transformer);
1360    // transformEntries(map3, transformer);
1361
1362    // Can't call with too loose a value:
1363    // transformEntries(map1, transformer);
1364    // transformEntries(map4, transformer);
1365    // transformEntries(map7, transformer);
1366  }
1367
1368  public void testTransformEntriesExample() {
1369    Map<String, Boolean> options = ImmutableMap.of("verbose", true, "sort", false);
1370    EntryTransformer<String, Boolean, String> flagPrefixer = new EntryTransformer<String, Boolean, String>() {
1371      @Override
1372      public String transformEntry(String key, Boolean value) {
1373        return value ? key : "no" + key;
1374      }
1375    };
1376    Map<String, String> transformed = transformEntries(options, flagPrefixer);
1377    assertEquals("{verbose=verbose, sort=nosort}", transformed.toString());
1378  }
1379
1380  // Logically this would accept a NavigableMap, but that won't work under GWT.
1381  private static <K, V> SortedMap<K, V> sortedNotNavigable(final SortedMap<K, V> map) {
1382    return new ForwardingSortedMap<K, V>() {
1383      @Override
1384      protected SortedMap<K, V> delegate() {
1385        return map;
1386      }
1387    };
1388  }
1389
1390  public void testSortedMapTransformValues() {
1391    SortedMap<String, Integer> map = sortedNotNavigable(ImmutableSortedMap.of("a", 4, "b", 9));
1392    SortedMap<String, Double> transformed = transformValues(map, SQRT_FUNCTION);
1393
1394    /*
1395     * We'd like to sanity check that we didn't get a NavigableMap out, but we
1396     * can't easily do so while maintaining GWT compatibility.
1397     */
1398    assertEquals(ImmutableSortedMap.of("a", 2.0, "b", 3.0), transformed);
1399  }
1400
1401  public void testSortedMapTransformEntries() {
1402    SortedMap<String, String> map = sortedNotNavigable(ImmutableSortedMap.of("a", "4", "b", "9"));
1403    EntryTransformer<String, String, String> concat = new EntryTransformer<String, String, String>() {
1404      @Override
1405      public String transformEntry(String key, String value) {
1406        return key + value;
1407      }
1408    };
1409    SortedMap<String, String> transformed = transformEntries(map, concat);
1410
1411    /*
1412     * We'd like to sanity check that we didn't get a NavigableMap out, but we
1413     * can't easily do so while maintaining GWT compatibility.
1414     */
1415    assertEquals(ImmutableSortedMap.of("a", "a4", "b", "b9"), transformed);
1416  }
1417
1418  // Hack for JDK5 type inference.
1419  private static class ASSERT {
1420    static <T> CollectionSubject<? extends CollectionSubject<?, T, Collection<T>>, T, Collection<T>> that(
1421        Collection<T> collection) {
1422      return Truth.ASSERT.<T, Collection<T>>that(collection);
1423    }
1424  }
1425}
1426