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