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