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