• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2008 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.testing.SerializableTester.reserialize;
20 import static com.google.common.truth.Truth.assertThat;
21 import static com.google.common.truth.Truth.assertWithMessage;
22 
23 import com.google.common.annotations.GwtCompatible;
24 import com.google.common.annotations.GwtIncompatible;
25 import com.google.common.collect.ImmutableMap.Builder;
26 import com.google.common.collect.testing.CollectionTestSuiteBuilder;
27 import com.google.common.collect.testing.ListTestSuiteBuilder;
28 import com.google.common.collect.testing.MapTestSuiteBuilder;
29 import com.google.common.collect.testing.features.CollectionFeature;
30 import com.google.common.collect.testing.features.CollectionSize;
31 import com.google.common.collect.testing.features.MapFeature;
32 import com.google.common.collect.testing.google.MapGenerators.ImmutableMapCopyOfEntriesGenerator;
33 import com.google.common.collect.testing.google.MapGenerators.ImmutableMapCopyOfEnumMapGenerator;
34 import com.google.common.collect.testing.google.MapGenerators.ImmutableMapCopyOfGenerator;
35 import com.google.common.collect.testing.google.MapGenerators.ImmutableMapEntryListGenerator;
36 import com.google.common.collect.testing.google.MapGenerators.ImmutableMapGenerator;
37 import com.google.common.collect.testing.google.MapGenerators.ImmutableMapKeyListGenerator;
38 import com.google.common.collect.testing.google.MapGenerators.ImmutableMapUnhashableValuesGenerator;
39 import com.google.common.collect.testing.google.MapGenerators.ImmutableMapValueListGenerator;
40 import com.google.common.collect.testing.google.MapGenerators.ImmutableMapValuesAsSingletonSetGenerator;
41 import com.google.common.testing.EqualsTester;
42 import com.google.common.testing.NullPointerTester;
43 import java.io.ByteArrayOutputStream;
44 import java.io.ObjectOutputStream;
45 import java.io.Serializable;
46 import java.util.AbstractMap;
47 import java.util.Arrays;
48 import java.util.Collection;
49 import java.util.Collections;
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.Set;
55 import java.util.regex.Matcher;
56 import java.util.regex.Pattern;
57 import junit.framework.Test;
58 import junit.framework.TestCase;
59 import junit.framework.TestSuite;
60 import org.checkerframework.checker.nullness.qual.Nullable;
61 
62 /**
63  * Tests for {@link ImmutableMap}.
64  *
65  * @author Kevin Bourrillion
66  * @author Jesse Wilson
67  */
68 @GwtCompatible(emulated = true)
69 @SuppressWarnings("AlwaysThrows")
70 public class ImmutableMapTest extends TestCase {
71 
72   @GwtIncompatible // suite
suite()73   public static Test suite() {
74     TestSuite suite = new TestSuite();
75     suite.addTestSuite(ImmutableMapTest.class);
76 
77     suite.addTest(
78         MapTestSuiteBuilder.using(new ImmutableMapGenerator())
79             .withFeatures(
80                 CollectionSize.ANY,
81                 CollectionFeature.SERIALIZABLE_INCLUDING_VIEWS,
82                 CollectionFeature.KNOWN_ORDER,
83                 MapFeature.REJECTS_DUPLICATES_AT_CREATION,
84                 CollectionFeature.ALLOWS_NULL_QUERIES)
85             .named("ImmutableMap")
86             .createTestSuite());
87 
88     suite.addTest(
89         MapTestSuiteBuilder.using(new ImmutableMapCopyOfGenerator())
90             .withFeatures(
91                 CollectionSize.ANY,
92                 CollectionFeature.SERIALIZABLE_INCLUDING_VIEWS,
93                 CollectionFeature.KNOWN_ORDER,
94                 CollectionFeature.ALLOWS_NULL_QUERIES)
95             .named("ImmutableMap.copyOf[Map]")
96             .createTestSuite());
97 
98     suite.addTest(
99         MapTestSuiteBuilder.using(new ImmutableMapCopyOfEntriesGenerator())
100             .withFeatures(
101                 CollectionSize.ANY,
102                 MapFeature.REJECTS_DUPLICATES_AT_CREATION,
103                 CollectionFeature.SERIALIZABLE_INCLUDING_VIEWS,
104                 CollectionFeature.KNOWN_ORDER,
105                 CollectionFeature.ALLOWS_NULL_QUERIES)
106             .named("ImmutableMap.copyOf[Iterable<Entry>]")
107             .createTestSuite());
108 
109     suite.addTest(
110         MapTestSuiteBuilder.using(new ImmutableMapCopyOfEnumMapGenerator())
111             .withFeatures(
112                 CollectionSize.ANY,
113                 CollectionFeature.SERIALIZABLE_INCLUDING_VIEWS,
114                 CollectionFeature.KNOWN_ORDER,
115                 CollectionFeature.ALLOWS_NULL_QUERIES)
116             .named("ImmutableMap.copyOf[EnumMap]")
117             .createTestSuite());
118 
119     suite.addTest(
120         MapTestSuiteBuilder.using(new ImmutableMapValuesAsSingletonSetGenerator())
121             .withFeatures(
122                 CollectionSize.ANY,
123                 MapFeature.REJECTS_DUPLICATES_AT_CREATION,
124                 CollectionFeature.KNOWN_ORDER,
125                 CollectionFeature.ALLOWS_NULL_QUERIES)
126             .named("ImmutableMap.asMultimap.asMap")
127             .createTestSuite());
128 
129     suite.addTest(
130         CollectionTestSuiteBuilder.using(new ImmutableMapUnhashableValuesGenerator())
131             .withFeatures(
132                 CollectionSize.ANY,
133                 CollectionFeature.KNOWN_ORDER,
134                 CollectionFeature.ALLOWS_NULL_QUERIES)
135             .named("ImmutableMap.values, unhashable")
136             .createTestSuite());
137 
138     suite.addTest(
139         ListTestSuiteBuilder.using(new ImmutableMapKeyListGenerator())
140             .named("ImmutableMap.keySet.asList")
141             .withFeatures(
142                 CollectionSize.ANY,
143                 CollectionFeature.SERIALIZABLE,
144                 CollectionFeature.REJECTS_DUPLICATES_AT_CREATION,
145                 CollectionFeature.ALLOWS_NULL_QUERIES)
146             .createTestSuite());
147 
148     suite.addTest(
149         ListTestSuiteBuilder.using(new ImmutableMapEntryListGenerator())
150             .named("ImmutableMap.entrySet.asList")
151             .withFeatures(
152                 CollectionSize.ANY,
153                 CollectionFeature.SERIALIZABLE,
154                 CollectionFeature.REJECTS_DUPLICATES_AT_CREATION,
155                 CollectionFeature.ALLOWS_NULL_QUERIES)
156             .createTestSuite());
157 
158     suite.addTest(
159         ListTestSuiteBuilder.using(new ImmutableMapValueListGenerator())
160             .named("ImmutableMap.values.asList")
161             .withFeatures(
162                 CollectionSize.ANY,
163                 CollectionFeature.SERIALIZABLE,
164                 CollectionFeature.ALLOWS_NULL_QUERIES)
165             .createTestSuite());
166 
167     return suite;
168   }
169 
170   // Creation tests
171 
testEmptyBuilder()172   public void testEmptyBuilder() {
173     ImmutableMap<String, Integer> map = new Builder<String, Integer>().buildOrThrow();
174     assertEquals(Collections.<String, Integer>emptyMap(), map);
175   }
176 
testSingletonBuilder()177   public void testSingletonBuilder() {
178     ImmutableMap<String, Integer> map = new Builder<String, Integer>().put("one", 1).buildOrThrow();
179     assertMapEquals(map, "one", 1);
180   }
181 
testBuilder()182   public void testBuilder() {
183     ImmutableMap<String, Integer> map =
184         new Builder<String, Integer>()
185             .put("one", 1)
186             .put("two", 2)
187             .put("three", 3)
188             .put("four", 4)
189             .put("five", 5)
190             .buildOrThrow();
191     assertMapEquals(map, "one", 1, "two", 2, "three", 3, "four", 4, "five", 5);
192   }
193 
194   @GwtIncompatible
testBuilderExactlySizedReusesArray()195   public void testBuilderExactlySizedReusesArray() {
196     ImmutableMap.Builder<Integer, Integer> builder = ImmutableMap.builderWithExpectedSize(10);
197     Object[] builderArray = builder.alternatingKeysAndValues;
198     for (int i = 0; i < 10; i++) {
199       builder.put(i, i);
200     }
201     Object[] builderArrayAfterPuts = builder.alternatingKeysAndValues;
202     RegularImmutableMap<Integer, Integer> map =
203         (RegularImmutableMap<Integer, Integer>) builder.buildOrThrow();
204     Object[] mapInternalArray = map.alternatingKeysAndValues;
205     assertSame(builderArray, builderArrayAfterPuts);
206     assertSame(builderArray, mapInternalArray);
207   }
208 
testBuilder_orderEntriesByValue()209   public void testBuilder_orderEntriesByValue() {
210     ImmutableMap<String, Integer> map =
211         new Builder<String, Integer>()
212             .orderEntriesByValue(Ordering.natural())
213             .put("three", 3)
214             .put("one", 1)
215             .put("five", 5)
216             .put("four", 4)
217             .put("two", 2)
218             .buildOrThrow();
219     assertMapEquals(map, "one", 1, "two", 2, "three", 3, "four", 4, "five", 5);
220   }
221 
testBuilder_orderEntriesByValueAfterExactSizeBuild()222   public void testBuilder_orderEntriesByValueAfterExactSizeBuild() {
223     Builder<String, Integer> builder = new Builder<String, Integer>(2).put("four", 4).put("one", 1);
224     ImmutableMap<String, Integer> keyOrdered = builder.buildOrThrow();
225     ImmutableMap<String, Integer> valueOrdered =
226         builder.orderEntriesByValue(Ordering.natural()).buildOrThrow();
227     assertMapEquals(keyOrdered, "four", 4, "one", 1);
228     assertMapEquals(valueOrdered, "one", 1, "four", 4);
229   }
230 
testBuilder_orderEntriesByValue_usedTwiceFails()231   public void testBuilder_orderEntriesByValue_usedTwiceFails() {
232     ImmutableMap.Builder<String, Integer> builder =
233         new Builder<String, Integer>().orderEntriesByValue(Ordering.natural());
234     try {
235       builder.orderEntriesByValue(Ordering.natural());
236       fail("Expected IllegalStateException");
237     } catch (IllegalStateException expected) {
238     }
239   }
240 
241   @GwtIncompatible // we haven't implemented this
testBuilder_orderEntriesByValue_keepingLast()242   public void testBuilder_orderEntriesByValue_keepingLast() {
243     ImmutableMap.Builder<String, Integer> builder =
244         new Builder<String, Integer>()
245             .orderEntriesByValue(Ordering.natural())
246             .put("three", 3)
247             .put("one", 1)
248             .put("five", 5)
249             .put("four", 3)
250             .put("four", 5)
251             .put("four", 4) // this should win because it's last
252             .put("two", 2);
253     assertMapEquals(
254         builder.buildKeepingLast(), "one", 1, "two", 2, "three", 3, "four", 4, "five", 5);
255     try {
256       builder.buildOrThrow();
257       fail("Expected exception from duplicate keys");
258     } catch (IllegalArgumentException expected) {
259     }
260   }
261 
262   @GwtIncompatible // we haven't implemented this
testBuilder_orderEntriesByValue_keepingLast_builderSizeFieldPreserved()263   public void testBuilder_orderEntriesByValue_keepingLast_builderSizeFieldPreserved() {
264     ImmutableMap.Builder<String, Integer> builder =
265         new Builder<String, Integer>()
266             .orderEntriesByValue(Ordering.natural())
267             .put("one", 1)
268             .put("one", 1);
269     assertMapEquals(builder.buildKeepingLast(), "one", 1);
270     try {
271       builder.buildOrThrow();
272       fail("Expected exception from duplicate keys");
273     } catch (IllegalArgumentException expected) {
274     }
275   }
276 
testBuilder_withImmutableEntry()277   public void testBuilder_withImmutableEntry() {
278     ImmutableMap<String, Integer> map =
279         new Builder<String, Integer>().put(Maps.immutableEntry("one", 1)).buildOrThrow();
280     assertMapEquals(map, "one", 1);
281   }
282 
testBuilder_withImmutableEntryAndNullContents()283   public void testBuilder_withImmutableEntryAndNullContents() {
284     Builder<String, Integer> builder = new Builder<>();
285     try {
286       builder.put(Maps.immutableEntry("one", (Integer) null));
287       fail();
288     } catch (NullPointerException expected) {
289     }
290     try {
291       builder.put(Maps.immutableEntry((String) null, 1));
292       fail();
293     } catch (NullPointerException expected) {
294     }
295   }
296 
297   private static class StringHolder {
298     String string;
299   }
300 
testBuilder_withMutableEntry()301   public void testBuilder_withMutableEntry() {
302     ImmutableMap.Builder<String, Integer> builder = new Builder<>();
303     final StringHolder holder = new StringHolder();
304     holder.string = "one";
305     Entry<String, Integer> entry =
306         new AbstractMapEntry<String, Integer>() {
307           @Override
308           public String getKey() {
309             return holder.string;
310           }
311 
312           @Override
313           public Integer getValue() {
314             return 1;
315           }
316         };
317 
318     builder.put(entry);
319     holder.string = "two";
320     assertMapEquals(builder.buildOrThrow(), "one", 1);
321   }
322 
testBuilderPutAllWithEmptyMap()323   public void testBuilderPutAllWithEmptyMap() {
324     ImmutableMap<String, Integer> map =
325         new Builder<String, Integer>()
326             .putAll(Collections.<String, Integer>emptyMap())
327             .buildOrThrow();
328     assertEquals(Collections.<String, Integer>emptyMap(), map);
329   }
330 
testBuilderPutAll()331   public void testBuilderPutAll() {
332     Map<String, Integer> toPut = new LinkedHashMap<>();
333     toPut.put("one", 1);
334     toPut.put("two", 2);
335     toPut.put("three", 3);
336     Map<String, Integer> moreToPut = new LinkedHashMap<>();
337     moreToPut.put("four", 4);
338     moreToPut.put("five", 5);
339 
340     ImmutableMap<String, Integer> map =
341         new Builder<String, Integer>().putAll(toPut).putAll(moreToPut).buildOrThrow();
342     assertMapEquals(map, "one", 1, "two", 2, "three", 3, "four", 4, "five", 5);
343   }
344 
testBuilderReuse()345   public void testBuilderReuse() {
346     Builder<String, Integer> builder = new Builder<>();
347     ImmutableMap<String, Integer> mapOne = builder.put("one", 1).put("two", 2).buildOrThrow();
348     ImmutableMap<String, Integer> mapTwo = builder.put("three", 3).put("four", 4).buildOrThrow();
349 
350     assertMapEquals(mapOne, "one", 1, "two", 2);
351     assertMapEquals(mapTwo, "one", 1, "two", 2, "three", 3, "four", 4);
352   }
353 
testBuilderPutNullKeyFailsAtomically()354   public void testBuilderPutNullKeyFailsAtomically() {
355     Builder<String, Integer> builder = new Builder<>();
356     try {
357       builder.put(null, 1);
358       fail();
359     } catch (NullPointerException expected) {
360     }
361     builder.put("foo", 2);
362     assertMapEquals(builder.buildOrThrow(), "foo", 2);
363   }
364 
testBuilderPutImmutableEntryWithNullKeyFailsAtomically()365   public void testBuilderPutImmutableEntryWithNullKeyFailsAtomically() {
366     Builder<String, Integer> builder = new Builder<>();
367     try {
368       builder.put(Maps.immutableEntry((String) null, 1));
369       fail();
370     } catch (NullPointerException expected) {
371     }
372     builder.put("foo", 2);
373     assertMapEquals(builder.buildOrThrow(), "foo", 2);
374   }
375 
376   // for GWT compatibility
377   static class SimpleEntry<K, V> extends AbstractMapEntry<K, V> {
378     public K key;
379     public V value;
380 
SimpleEntry(K key, V value)381     SimpleEntry(K key, V value) {
382       this.key = key;
383       this.value = value;
384     }
385 
386     @Override
getKey()387     public K getKey() {
388       return key;
389     }
390 
391     @Override
getValue()392     public V getValue() {
393       return value;
394     }
395   }
396 
testBuilderPutMutableEntryWithNullKeyFailsAtomically()397   public void testBuilderPutMutableEntryWithNullKeyFailsAtomically() {
398     Builder<String, Integer> builder = new Builder<>();
399     try {
400       builder.put(new SimpleEntry<String, Integer>(null, 1));
401       fail();
402     } catch (NullPointerException expected) {
403     }
404     builder.put("foo", 2);
405     assertMapEquals(builder.buildOrThrow(), "foo", 2);
406   }
407 
testBuilderPutNullKey()408   public void testBuilderPutNullKey() {
409     Builder<String, Integer> builder = new Builder<>();
410     try {
411       builder.put(null, 1);
412       fail();
413     } catch (NullPointerException expected) {
414     }
415   }
416 
testBuilderPutNullValue()417   public void testBuilderPutNullValue() {
418     Builder<String, Integer> builder = new Builder<>();
419     try {
420       builder.put("one", null);
421       fail();
422     } catch (NullPointerException expected) {
423     }
424   }
425 
testBuilderPutNullKeyViaPutAll()426   public void testBuilderPutNullKeyViaPutAll() {
427     Builder<String, Integer> builder = new Builder<>();
428     try {
429       builder.putAll(Collections.<String, Integer>singletonMap(null, 1));
430       fail();
431     } catch (NullPointerException expected) {
432     }
433   }
434 
testBuilderPutNullValueViaPutAll()435   public void testBuilderPutNullValueViaPutAll() {
436     Builder<String, Integer> builder = new Builder<>();
437     try {
438       builder.putAll(Collections.<String, Integer>singletonMap("one", null));
439       fail();
440     } catch (NullPointerException expected) {
441     }
442   }
443 
testPuttingTheSameKeyTwiceThrowsOnBuild()444   public void testPuttingTheSameKeyTwiceThrowsOnBuild() {
445     Builder<String, Integer> builder =
446         new Builder<String, Integer>()
447             .put("one", 1)
448             .put("one", 1); // throwing on this line might be better but it's too late to change
449 
450     try {
451       builder.buildOrThrow();
452       fail();
453     } catch (IllegalArgumentException expected) {
454     }
455   }
456 
testBuildKeepingLast_allowsOverwrite()457   public void testBuildKeepingLast_allowsOverwrite() {
458     Builder<Integer, String> builder =
459         new Builder<Integer, String>()
460             .put(1, "un")
461             .put(2, "deux")
462             .put(70, "soixante-dix")
463             .put(70, "septante")
464             .put(70, "seventy")
465             .put(1, "one")
466             .put(2, "two");
467     ImmutableMap<Integer, String> map = builder.buildKeepingLast();
468     assertMapEquals(map, 1, "one", 2, "two", 70, "seventy");
469   }
470 
testBuildKeepingLast_smallTableSameHash()471   public void testBuildKeepingLast_smallTableSameHash() {
472     String key1 = "QED";
473     String key2 = "R&D";
474     assertThat(key1.hashCode()).isEqualTo(key2.hashCode());
475     ImmutableMap<String, Integer> map =
476         ImmutableMap.<String, Integer>builder()
477             .put(key1, 1)
478             .put(key2, 2)
479             .put(key1, 3)
480             .put(key2, 4)
481             .buildKeepingLast();
482     assertMapEquals(map, key1, 3, key2, 4);
483   }
484 
485   // The java7 branch has different code depending on whether the entry indexes fit in a byte,
486   // short, or int. The small table in testBuildKeepingLast_allowsOverwrite will test the byte
487   // case. This method tests the short case.
testBuildKeepingLast_shortTable()488   public void testBuildKeepingLast_shortTable() {
489     Builder<Integer, String> builder = ImmutableMap.builder();
490     Map<Integer, String> expected = new LinkedHashMap<>();
491     for (int i = 0; i < 1000; i++) {
492       // Truncate to even key, so we have put(0, "0") then put(0, "1"). Half the entries are
493       // duplicates.
494       Integer key = i & ~1;
495       String value = String.valueOf(i);
496       builder.put(key, value);
497       expected.put(key, value);
498     }
499     ImmutableMap<Integer, String> map = builder.buildKeepingLast();
500     assertThat(map).hasSize(500);
501     assertThat(map).containsExactlyEntriesIn(expected).inOrder();
502   }
503 
504   // This method tests the int case.
testBuildKeepingLast_bigTable()505   public void testBuildKeepingLast_bigTable() {
506     Builder<Integer, String> builder = ImmutableMap.builder();
507     Map<Integer, String> expected = new LinkedHashMap<>();
508     for (int i = 0; i < 200_000; i++) {
509       // Truncate to even key, so we have put(0, "0") then put(0, "1"). Half the entries are
510       // duplicates.
511       Integer key = i & ~1;
512       String value = String.valueOf(i);
513       builder.put(key, value);
514       expected.put(key, value);
515     }
516     ImmutableMap<Integer, String> map = builder.buildKeepingLast();
517     assertThat(map).hasSize(100_000);
518     assertThat(map).containsExactlyEntriesIn(expected).inOrder();
519   }
520 
521   private static class ClassWithTerribleHashCode implements Comparable<ClassWithTerribleHashCode> {
522     private final int value;
523 
ClassWithTerribleHashCode(int value)524     ClassWithTerribleHashCode(int value) {
525       this.value = value;
526     }
527 
528     @Override
compareTo(ClassWithTerribleHashCode that)529     public int compareTo(ClassWithTerribleHashCode that) {
530       return Integer.compare(this.value, that.value);
531     }
532 
533     @Override
equals(@ullable Object x)534     public boolean equals(@Nullable Object x) {
535       return x instanceof ClassWithTerribleHashCode
536           && ((ClassWithTerribleHashCode) x).value == value;
537     }
538 
539     @Override
hashCode()540     public int hashCode() {
541       return 23;
542     }
543 
544     @Override
toString()545     public String toString() {
546       return "ClassWithTerribleHashCode(" + value + ")";
547     }
548   }
549 
550   @GwtIncompatible
testBuildKeepingLast_collisions()551   public void testBuildKeepingLast_collisions() {
552     Map<ClassWithTerribleHashCode, Integer> expected = new LinkedHashMap<>();
553     Builder<ClassWithTerribleHashCode, Integer> builder = new Builder<>();
554     int size = 18;
555     for (int i = 0; i < size; i++) {
556       ClassWithTerribleHashCode key = new ClassWithTerribleHashCode(i);
557       builder.put(key, i);
558       builder.put(key, -i);
559       expected.put(key, -i);
560     }
561     ImmutableMap<ClassWithTerribleHashCode, Integer> map = builder.buildKeepingLast();
562     assertThat(map).containsExactlyEntriesIn(expected).inOrder();
563   }
564 
565   @GwtIncompatible // Pattern, Matcher
testBuilder_keepingLast_thenOrThrow()566   public void testBuilder_keepingLast_thenOrThrow() {
567     ImmutableMap.Builder<String, Integer> builder =
568         new Builder<String, Integer>()
569             .put("three", 3)
570             .put("one", 1)
571             .put("five", 5)
572             .put("four", 3)
573             .put("four", 5)
574             .put("four", 4) // this should win because it's last
575             .put("two", 2);
576     assertMapEquals(
577         builder.buildKeepingLast(), "three", 3, "one", 1, "five", 5, "four", 4, "two", 2);
578     try {
579       builder.buildOrThrow();
580       fail("Expected exception from duplicate keys");
581     } catch (IllegalArgumentException expected) {
582       // We don't really care which values the exception message contains, but they should be
583       // different from each other. If buildKeepingLast() collapsed duplicates, that might end up
584       // not being true.
585       Pattern pattern = Pattern.compile("Multiple entries with same key: four=(.*) and four=(.*)");
586       assertThat(expected).hasMessageThat().matches(pattern);
587       Matcher matcher = pattern.matcher(expected.getMessage());
588       assertThat(matcher.matches()).isTrue();
589       assertThat(matcher.group(1)).isNotEqualTo(matcher.group(2));
590     }
591   }
592 
testOf()593   public void testOf() {
594     assertMapEquals(ImmutableMap.of("one", 1), "one", 1);
595     assertMapEquals(ImmutableMap.of("one", 1, "two", 2), "one", 1, "two", 2);
596     assertMapEquals(
597         ImmutableMap.of("one", 1, "two", 2, "three", 3), "one", 1, "two", 2, "three", 3);
598     assertMapEquals(
599         ImmutableMap.of("one", 1, "two", 2, "three", 3, "four", 4),
600         "one",
601         1,
602         "two",
603         2,
604         "three",
605         3,
606         "four",
607         4);
608     assertMapEquals(
609         ImmutableMap.of("one", 1, "two", 2, "three", 3, "four", 4, "five", 5),
610         "one",
611         1,
612         "two",
613         2,
614         "three",
615         3,
616         "four",
617         4,
618         "five",
619         5);
620     assertMapEquals(
621         ImmutableMap.of(
622             "one", 1,
623             "two", 2,
624             "three", 3,
625             "four", 4,
626             "five", 5,
627             "six", 6),
628         "one",
629         1,
630         "two",
631         2,
632         "three",
633         3,
634         "four",
635         4,
636         "five",
637         5,
638         "six",
639         6);
640     assertMapEquals(
641         ImmutableMap.of(
642             "one", 1,
643             "two", 2,
644             "three", 3,
645             "four", 4,
646             "five", 5,
647             "six", 6,
648             "seven", 7),
649         "one",
650         1,
651         "two",
652         2,
653         "three",
654         3,
655         "four",
656         4,
657         "five",
658         5,
659         "six",
660         6,
661         "seven",
662         7);
663     assertMapEquals(
664         ImmutableMap.of(
665             "one", 1,
666             "two", 2,
667             "three", 3,
668             "four", 4,
669             "five", 5,
670             "six", 6,
671             "seven", 7,
672             "eight", 8),
673         "one",
674         1,
675         "two",
676         2,
677         "three",
678         3,
679         "four",
680         4,
681         "five",
682         5,
683         "six",
684         6,
685         "seven",
686         7,
687         "eight",
688         8);
689     assertMapEquals(
690         ImmutableMap.of(
691             "one", 1,
692             "two", 2,
693             "three", 3,
694             "four", 4,
695             "five", 5,
696             "six", 6,
697             "seven", 7,
698             "eight", 8,
699             "nine", 9),
700         "one",
701         1,
702         "two",
703         2,
704         "three",
705         3,
706         "four",
707         4,
708         "five",
709         5,
710         "six",
711         6,
712         "seven",
713         7,
714         "eight",
715         8,
716         "nine",
717         9);
718     assertMapEquals(
719         ImmutableMap.of(
720             "one", 1,
721             "two", 2,
722             "three", 3,
723             "four", 4,
724             "five", 5,
725             "six", 6,
726             "seven", 7,
727             "eight", 8,
728             "nine", 9,
729             "ten", 10),
730         "one",
731         1,
732         "two",
733         2,
734         "three",
735         3,
736         "four",
737         4,
738         "five",
739         5,
740         "six",
741         6,
742         "seven",
743         7,
744         "eight",
745         8,
746         "nine",
747         9,
748         "ten",
749         10);
750   }
751 
testOfNullKey()752   public void testOfNullKey() {
753     try {
754       ImmutableMap.of(null, 1);
755       fail();
756     } catch (NullPointerException expected) {
757     }
758 
759     try {
760       ImmutableMap.of("one", 1, null, 2);
761       fail();
762     } catch (NullPointerException expected) {
763     }
764   }
765 
testOfNullValue()766   public void testOfNullValue() {
767     try {
768       ImmutableMap.of("one", null);
769       fail();
770     } catch (NullPointerException expected) {
771     }
772 
773     try {
774       ImmutableMap.of("one", 1, "two", null);
775       fail();
776     } catch (NullPointerException expected) {
777     }
778   }
779 
testOfWithDuplicateKey()780   public void testOfWithDuplicateKey() {
781     try {
782       ImmutableMap.of("one", 1, "one", 1);
783       fail();
784     } catch (IllegalArgumentException expected) {
785     }
786   }
787 
testCopyOfEmptyMap()788   public void testCopyOfEmptyMap() {
789     ImmutableMap<String, Integer> copy =
790         ImmutableMap.copyOf(Collections.<String, Integer>emptyMap());
791     assertEquals(Collections.<String, Integer>emptyMap(), copy);
792     assertSame(copy, ImmutableMap.copyOf(copy));
793   }
794 
testCopyOfSingletonMap()795   public void testCopyOfSingletonMap() {
796     ImmutableMap<String, Integer> copy = ImmutableMap.copyOf(Collections.singletonMap("one", 1));
797     assertMapEquals(copy, "one", 1);
798     assertSame(copy, ImmutableMap.copyOf(copy));
799   }
800 
testCopyOf()801   public void testCopyOf() {
802     Map<String, Integer> original = new LinkedHashMap<>();
803     original.put("one", 1);
804     original.put("two", 2);
805     original.put("three", 3);
806 
807     ImmutableMap<String, Integer> copy = ImmutableMap.copyOf(original);
808     assertMapEquals(copy, "one", 1, "two", 2, "three", 3);
809     assertSame(copy, ImmutableMap.copyOf(copy));
810   }
811 
812   // TODO(b/172823566): Use mainline testToImmutableMap once CollectorTester is usable to java7.
testToImmutableMap_java7_combine()813   public void testToImmutableMap_java7_combine() {
814     ImmutableMap.Builder<String, Integer> zis =
815         ImmutableMap.<String, Integer>builder().put("one", 1);
816     ImmutableMap.Builder<String, Integer> zat =
817         ImmutableMap.<String, Integer>builder().put("two", 2).put("three", 3);
818     assertMapEquals(zis.combine(zat).build(), "one", 1, "two", 2, "three", 3);
819   }
820 
821   // TODO(b/172823566): Use mainline testToImmutableMap once CollectorTester is usable to java7.
testToImmutableMap_exceptionOnDuplicateKey_java7_combine()822   public void testToImmutableMap_exceptionOnDuplicateKey_java7_combine() {
823     ImmutableMap.Builder<String, Integer> zis =
824         ImmutableMap.<String, Integer>builder().put("one", 1).put("two", 2);
825     ImmutableMap.Builder<String, Integer> zat =
826         ImmutableMap.<String, Integer>builder().put("two", 22).put("three", 3);
827     try {
828       zis.combine(zat).build();
829       fail("Expected IllegalArgumentException");
830     } catch (IllegalArgumentException expected) {
831       // expected
832     }
833   }
834 
hashtableTestHelper(ImmutableList<Integer> sizes)835   public static void hashtableTestHelper(ImmutableList<Integer> sizes) {
836     for (int size : sizes) {
837       Builder<Integer, Integer> builder = ImmutableMap.builderWithExpectedSize(size);
838       for (int i = 0; i < size; i++) {
839         Integer integer = i;
840         builder.put(integer, integer);
841       }
842       ImmutableMap<Integer, Integer> map = builder.build();
843       assertEquals(size, map.size());
844       int entries = 0;
845       for (Integer key : map.keySet()) {
846         assertEquals(entries, key.intValue());
847         assertSame(key, map.get(key));
848         entries++;
849       }
850       assertEquals(size, entries);
851     }
852   }
853 
testByteArrayHashtable()854   public void testByteArrayHashtable() {
855     hashtableTestHelper(ImmutableList.of(2, 89));
856   }
857 
testShortArrayHashtable()858   public void testShortArrayHashtable() {
859     hashtableTestHelper(ImmutableList.of(90, 22937));
860   }
861 
testIntArrayHashtable()862   public void testIntArrayHashtable() {
863     hashtableTestHelper(ImmutableList.of(22938));
864   }
865 
866   // Non-creation tests
867 
testNullGet()868   public void testNullGet() {
869     ImmutableMap<String, Integer> map = ImmutableMap.of("one", 1);
870     assertNull(map.get(null));
871   }
872 
testAsMultimap()873   public void testAsMultimap() {
874     ImmutableMap<String, Integer> map =
875         ImmutableMap.of("one", 1, "won", 1, "two", 2, "too", 2, "three", 3);
876     ImmutableSetMultimap<String, Integer> expected =
877         ImmutableSetMultimap.of("one", 1, "won", 1, "two", 2, "too", 2, "three", 3);
878     assertEquals(expected, map.asMultimap());
879   }
880 
testAsMultimapWhenEmpty()881   public void testAsMultimapWhenEmpty() {
882     ImmutableMap<String, Integer> map = ImmutableMap.of();
883     ImmutableSetMultimap<String, Integer> expected = ImmutableSetMultimap.of();
884     assertEquals(expected, map.asMultimap());
885   }
886 
testAsMultimapCaches()887   public void testAsMultimapCaches() {
888     ImmutableMap<String, Integer> map = ImmutableMap.of("one", 1);
889     ImmutableSetMultimap<String, Integer> multimap1 = map.asMultimap();
890     ImmutableSetMultimap<String, Integer> multimap2 = map.asMultimap();
891     assertEquals(1, multimap1.asMap().size());
892     assertSame(multimap1, multimap2);
893   }
894 
895   @GwtIncompatible // NullPointerTester
896   @AndroidIncompatible // see ImmutableTableTest.testNullPointerInstance
testNullPointers()897   public void testNullPointers() {
898     NullPointerTester tester = new NullPointerTester();
899     tester.testAllPublicStaticMethods(ImmutableMap.class);
900     tester.testAllPublicInstanceMethods(new ImmutableMap.Builder<Object, Object>());
901     tester.testAllPublicInstanceMethods(ImmutableMap.of());
902     tester.testAllPublicInstanceMethods(ImmutableMap.of("one", 1));
903     tester.testAllPublicInstanceMethods(ImmutableMap.of("one", 1, "two", 2, "three", 3));
904   }
905 
assertMapEquals(Map<K, V> map, Object... alternatingKeysAndValues)906   private static <K, V> void assertMapEquals(Map<K, V> map, Object... alternatingKeysAndValues) {
907     Map<Object, Object> expected = new LinkedHashMap<>();
908     for (int i = 0; i < alternatingKeysAndValues.length; i += 2) {
909       expected.put(alternatingKeysAndValues[i], alternatingKeysAndValues[i + 1]);
910     }
911     assertThat(map).containsExactlyEntriesIn(expected).inOrder();
912   }
913 
914   private static class IntHolder implements Serializable {
915     public int value;
916 
IntHolder(int value)917     public IntHolder(int value) {
918       this.value = value;
919     }
920 
921     @Override
equals(@ullable Object o)922     public boolean equals(@Nullable Object o) {
923       return (o instanceof IntHolder) && ((IntHolder) o).value == value;
924     }
925 
926     @Override
hashCode()927     public int hashCode() {
928       return value;
929     }
930 
931     private static final long serialVersionUID = 5;
932   }
933 
testMutableValues()934   public void testMutableValues() {
935     IntHolder holderA = new IntHolder(1);
936     IntHolder holderB = new IntHolder(2);
937     Map<String, IntHolder> map = ImmutableMap.of("a", holderA, "b", holderB);
938     holderA.value = 3;
939     assertTrue(map.entrySet().contains(Maps.immutableEntry("a", new IntHolder(3))));
940     Map<String, Integer> intMap = ImmutableMap.of("a", 3, "b", 2);
941     assertEquals(intMap.hashCode(), map.entrySet().hashCode());
942     assertEquals(intMap.hashCode(), map.hashCode());
943   }
944 
945   @GwtIncompatible // SerializableTester
testViewSerialization()946   public void testViewSerialization() {
947     Map<String, Integer> map = ImmutableMap.of("one", 1, "two", 2, "three", 3);
948     LenientSerializableTester.reserializeAndAssertLenient(map.entrySet());
949     LenientSerializableTester.reserializeAndAssertLenient(map.keySet());
950 
951     Collection<Integer> reserializedValues = reserialize(map.values());
952     assertEquals(Lists.newArrayList(map.values()), Lists.newArrayList(reserializedValues));
953     assertTrue(reserializedValues instanceof ImmutableCollection);
954   }
955 
956   @GwtIncompatible // SerializableTester
testKeySetIsSerializable_regularImmutableMap()957   public void testKeySetIsSerializable_regularImmutableMap() {
958     class NonSerializableClass {}
959 
960     Map<String, NonSerializableClass> map =
961         RegularImmutableMap.create(1, new Object[] {"one", new NonSerializableClass()});
962     Set<String> set = map.keySet();
963 
964     LenientSerializableTester.reserializeAndAssertLenient(set);
965   }
966 
967   @GwtIncompatible // SerializableTester
testValuesCollectionIsSerializable_regularImmutableMap()968   public void testValuesCollectionIsSerializable_regularImmutableMap() {
969     class NonSerializableClass {}
970 
971     Map<NonSerializableClass, String> map =
972         RegularImmutableMap.create(1, new Object[] {new NonSerializableClass(), "value"});
973     Collection<String> collection = map.values();
974 
975     LenientSerializableTester.reserializeAndAssertElementsEqual(collection);
976   }
977 
978   // TODO: Re-enable this test after moving to new serialization format in ImmutableMap.
979   @GwtIncompatible // SerializableTester
980   @SuppressWarnings("unchecked")
ignore_testSerializationNoDuplication_regularImmutableMap()981   public void ignore_testSerializationNoDuplication_regularImmutableMap() throws Exception {
982     // Tests that serializing a map, its keySet, and values only writes the underlying data once.
983 
984     Object[] entries = new Object[2000];
985     for (int i = 0; i < entries.length; i++) {
986       entries[i] = i;
987     }
988 
989     ImmutableMap<Integer, Integer> map = RegularImmutableMap.create(entries.length / 2, entries);
990     Set<Integer> keySet = map.keySet();
991     Collection<Integer> values = map.values();
992 
993     ByteArrayOutputStream bytes = new ByteArrayOutputStream();
994     ObjectOutputStream oos = new ObjectOutputStream(bytes);
995     oos.writeObject(map);
996     oos.flush();
997 
998     int mapSize = bytes.size();
999     oos.writeObject(keySet);
1000     oos.writeObject(values);
1001     oos.close();
1002 
1003     int finalSize = bytes.size();
1004 
1005     assertThat(finalSize - mapSize).isLessThan(100);
1006   }
1007 
testEquals()1008   public void testEquals() {
1009     new EqualsTester()
1010         .addEqualityGroup(
1011             ImmutableMap.of(),
1012             ImmutableMap.builder().buildOrThrow(),
1013             ImmutableMap.ofEntries(),
1014             map())
1015         .addEqualityGroup(
1016             ImmutableMap.of(1, 1),
1017             ImmutableMap.builder().put(1, 1).buildOrThrow(),
1018             ImmutableMap.ofEntries(entry(1, 1)),
1019             map(1, 1))
1020         .addEqualityGroup(
1021             ImmutableMap.of(1, 1, 2, 2),
1022             ImmutableMap.builder().put(1, 1).put(2, 2).buildOrThrow(),
1023             ImmutableMap.ofEntries(entry(1, 1), entry(2, 2)),
1024             map(1, 1, 2, 2))
1025         .addEqualityGroup(
1026             ImmutableMap.of(1, 1, 2, 2, 3, 3),
1027             ImmutableMap.builder().put(1, 1).put(2, 2).put(3, 3).buildOrThrow(),
1028             ImmutableMap.ofEntries(entry(1, 1), entry(2, 2), entry(3, 3)),
1029             map(1, 1, 2, 2, 3, 3))
1030         .addEqualityGroup(
1031             ImmutableMap.of(1, 4, 2, 2, 3, 3),
1032             ImmutableMap.builder().put(1, 4).put(2, 2).put(3, 3).buildOrThrow(),
1033             ImmutableMap.ofEntries(entry(1, 4), entry(2, 2), entry(3, 3)),
1034             map(1, 4, 2, 2, 3, 3))
1035         .addEqualityGroup(
1036             ImmutableMap.of(1, 1, 2, 4, 3, 3),
1037             ImmutableMap.builder().put(1, 1).put(2, 4).put(3, 3).buildOrThrow(),
1038             ImmutableMap.ofEntries(entry(1, 1), entry(2, 4), entry(3, 3)),
1039             map(1, 1, 2, 4, 3, 3))
1040         .addEqualityGroup(
1041             ImmutableMap.of(1, 1, 2, 2, 3, 4),
1042             ImmutableMap.builder().put(1, 1).put(2, 2).put(3, 4).buildOrThrow(),
1043             ImmutableMap.ofEntries(entry(1, 1), entry(2, 2), entry(3, 4)),
1044             map(1, 1, 2, 2, 3, 4))
1045         .addEqualityGroup(
1046             ImmutableMap.of(1, 2, 2, 3, 3, 1),
1047             ImmutableMap.builder().put(1, 2).put(2, 3).put(3, 1).buildOrThrow(),
1048             ImmutableMap.ofEntries(entry(1, 2), entry(2, 3), entry(3, 1)),
1049             map(1, 2, 2, 3, 3, 1))
1050         .addEqualityGroup(
1051             ImmutableMap.of(1, 1, 2, 2, 3, 3, 4, 4),
1052             ImmutableMap.builder().put(1, 1).put(2, 2).put(3, 3).put(4, 4).buildOrThrow(),
1053             ImmutableMap.ofEntries(entry(1, 1), entry(2, 2), entry(3, 3), entry(4, 4)),
1054             map(1, 1, 2, 2, 3, 3, 4, 4))
1055         .addEqualityGroup(
1056             ImmutableMap.of(1, 1, 2, 2, 3, 3, 4, 4, 5, 5),
1057             ImmutableMap.builder().put(1, 1).put(2, 2).put(3, 3).put(4, 4).put(5, 5).buildOrThrow(),
1058             ImmutableMap.ofEntries(entry(1, 1), entry(2, 2), entry(3, 3), entry(4, 4), entry(5, 5)),
1059             map(1, 1, 2, 2, 3, 3, 4, 4, 5, 5))
1060         .testEquals();
1061   }
1062 
testOfEntriesNull()1063   public void testOfEntriesNull() {
1064     Entry<Integer, Integer> nullKey = entry(null, 23);
1065     try {
1066       ImmutableMap.ofEntries(nullKey);
1067       fail();
1068     } catch (NullPointerException expected) {
1069     }
1070     Entry<Integer, Integer> nullValue = entry(23, null);
1071     try {
1072       ImmutableMap.ofEntries(nullValue);
1073       fail();
1074     } catch (NullPointerException expected) {
1075     }
1076   }
1077 
map(T... keysAndValues)1078   private static <T> Map<T, T> map(T... keysAndValues) {
1079     assertThat(keysAndValues.length % 2).isEqualTo(0);
1080     LinkedHashMap<T, T> map = new LinkedHashMap<>();
1081     for (int i = 0; i < keysAndValues.length; i += 2) {
1082       T key = keysAndValues[i];
1083       T value = keysAndValues[i + 1];
1084       T old = map.put(key, value);
1085       assertWithMessage("Key %s set to %s and %s", key, value, old).that(old).isNull();
1086     }
1087     return map;
1088   }
1089 
entry(T key, T value)1090   private static <T> Entry<T, T> entry(T key, T value) {
1091     return new AbstractMap.SimpleImmutableEntry<>(key, value);
1092   }
1093 
testCopyOfMutableEntryList()1094   public void testCopyOfMutableEntryList() {
1095     List<Entry<String, String>> entryList =
1096         Arrays.asList(
1097             new AbstractMap.SimpleEntry<>("a", "1"), new AbstractMap.SimpleEntry<>("b", "2"));
1098     ImmutableMap<String, String> map = ImmutableMap.copyOf(entryList);
1099     assertThat(map).containsExactly("a", "1", "b", "2").inOrder();
1100     entryList.get(0).setValue("3");
1101     assertThat(map).containsExactly("a", "1", "b", "2").inOrder();
1102   }
1103 
testBuilderPutAllEntryList()1104   public void testBuilderPutAllEntryList() {
1105     List<Entry<String, String>> entryList =
1106         Arrays.asList(
1107             new AbstractMap.SimpleEntry<>("a", "1"), new AbstractMap.SimpleEntry<>("b", "2"));
1108     ImmutableMap<String, String> map =
1109         ImmutableMap.<String, String>builder().putAll(entryList).buildOrThrow();
1110     assertThat(map).containsExactly("a", "1", "b", "2").inOrder();
1111     entryList.get(0).setValue("3");
1112     assertThat(map).containsExactly("a", "1", "b", "2").inOrder();
1113   }
1114 }
1115