View Javadoc
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 com.google.common.annotations.GwtCompatible;
20  import com.google.common.base.Joiner;
21  import com.google.common.collect.ImmutableMap.Builder;
22  import com.google.common.collect.testing.AnEnum;
23  import com.google.common.collect.testing.MapInterfaceTest;
24  import com.google.common.collect.testing.MinimalSet;
25  import com.google.common.collect.testing.SampleElements.Colliders;
26  import com.google.common.testing.EqualsTester;
27  
28  import junit.framework.TestCase;
29  
30  import java.io.Serializable;
31  import java.util.Collections;
32  import java.util.EnumMap;
33  import java.util.LinkedHashMap;
34  import java.util.Map;
35  import java.util.Map.Entry;
36  
37  /**
38   * Tests for {@link ImmutableMap}.
39   *
40   * @author Kevin Bourrillion
41   * @author Jesse Wilson
42   */
43  @GwtCompatible(emulated = true)
44  public class ImmutableMapTest extends TestCase {
45  
46    public abstract static class AbstractMapTests<K, V>
47        extends MapInterfaceTest<K, V> {
48      public AbstractMapTests() {
49        super(false, false, false, false, false);
50      }
51  
52      @Override protected Map<K, V> makeEmptyMap() {
53        throw new UnsupportedOperationException();
54      }
55  
56      private static final Joiner joiner = Joiner.on(", ");
57  
58      @Override protected void assertMoreInvariants(Map<K, V> map) {
59        // TODO: can these be moved to MapInterfaceTest?
60        for (Entry<K, V> entry : map.entrySet()) {
61          assertEquals(entry.getKey() + "=" + entry.getValue(),
62              entry.toString());
63        }
64  
65        assertEquals("{" + joiner.join(map.entrySet()) + "}",
66            map.toString());
67        assertEquals("[" + joiner.join(map.entrySet()) + "]",
68            map.entrySet().toString());
69        assertEquals("[" + joiner.join(map.keySet()) + "]",
70            map.keySet().toString());
71        assertEquals("[" + joiner.join(map.values()) + "]",
72            map.values().toString());
73  
74        assertEquals(MinimalSet.from(map.entrySet()), map.entrySet());
75        assertEquals(Sets.newHashSet(map.keySet()), map.keySet());
76      }
77    }
78  
79    public static class MapTests extends AbstractMapTests<String, Integer> {
80      @Override protected Map<String, Integer> makeEmptyMap() {
81        return ImmutableMap.of();
82      }
83  
84      @Override protected Map<String, Integer> makePopulatedMap() {
85        return ImmutableMap.of("one", 1, "two", 2, "three", 3);
86      }
87  
88      @Override protected String getKeyNotInPopulatedMap() {
89        return "minus one";
90      }
91  
92      @Override protected Integer getValueNotInPopulatedMap() {
93        return -1;
94      }
95    }
96  
97    public static class SingletonMapTests
98        extends AbstractMapTests<String, Integer> {
99      @Override protected Map<String, Integer> makePopulatedMap() {
100       return ImmutableMap.of("one", 1);
101     }
102 
103     @Override protected String getKeyNotInPopulatedMap() {
104       return "minus one";
105     }
106 
107     @Override protected Integer getValueNotInPopulatedMap() {
108       return -1;
109     }
110   }
111 
112   public static class MapTestsWithBadHashes
113       extends AbstractMapTests<Object, Integer> {
114 
115     @Override protected Map<Object, Integer> makeEmptyMap() {
116       throw new UnsupportedOperationException();
117     }
118 
119     @Override protected Map<Object, Integer> makePopulatedMap() {
120       Colliders colliders = new Colliders();
121       return ImmutableMap.of(
122           colliders.e0, 0,
123           colliders.e1, 1,
124           colliders.e2, 2,
125           colliders.e3, 3);
126     }
127 
128     @Override protected Object getKeyNotInPopulatedMap() {
129       return new Colliders().e4;
130     }
131 
132     @Override protected Integer getValueNotInPopulatedMap() {
133       return 4;
134     }
135   }
136 
137   public static class CreationTests extends TestCase {
138     public void testEmptyBuilder() {
139       ImmutableMap<String, Integer> map
140           = new Builder<String, Integer>().build();
141       assertEquals(Collections.<String, Integer>emptyMap(), map);
142     }
143 
144     public void testSingletonBuilder() {
145       ImmutableMap<String, Integer> map = new Builder<String, Integer>()
146           .put("one", 1)
147           .build();
148       assertMapEquals(map, "one", 1);
149     }
150 
151     public void testBuilder() {
152       ImmutableMap<String, Integer> map = new Builder<String, Integer>()
153           .put("one", 1)
154           .put("two", 2)
155           .put("three", 3)
156           .put("four", 4)
157           .put("five", 5)
158           .build();
159       assertMapEquals(map,
160           "one", 1, "two", 2, "three", 3, "four", 4, "five", 5);
161     }
162 
163     public void testBuilder_withImmutableEntry() {
164       ImmutableMap<String, Integer> map = new Builder<String, Integer>()
165           .put(Maps.immutableEntry("one", 1))
166           .build();
167       assertMapEquals(map, "one", 1);
168     }
169 
170     public void testBuilder_withImmutableEntryAndNullContents() {
171       Builder<String, Integer> builder = new Builder<String, Integer>();
172       try {
173         builder.put(Maps.immutableEntry("one", (Integer) null));
174         fail();
175       } catch (NullPointerException expected) {
176       }
177       try {
178         builder.put(Maps.immutableEntry((String) null, 1));
179         fail();
180       } catch (NullPointerException expected) {
181       }
182     }
183 
184     private static class StringHolder {
185       String string;
186     }
187 
188     public void testBuilder_withMutableEntry() {
189       ImmutableMap.Builder<String, Integer> builder =
190           new Builder<String, Integer>();
191       final StringHolder holder = new StringHolder();
192       holder.string = "one";
193       Entry<String, Integer> entry = new AbstractMapEntry<String, Integer>() {
194         @Override public String getKey() {
195           return holder.string;
196         }
197         @Override public Integer getValue() {
198           return 1;
199         }
200       };
201 
202       builder.put(entry);
203       holder.string = "two";
204       assertMapEquals(builder.build(), "one", 1);
205     }
206 
207     public void testBuilderPutAllWithEmptyMap() {
208       ImmutableMap<String, Integer> map = new Builder<String, Integer>()
209           .putAll(Collections.<String, Integer>emptyMap())
210           .build();
211       assertEquals(Collections.<String, Integer>emptyMap(), map);
212     }
213 
214     public void testBuilderPutAll() {
215       Map<String, Integer> toPut = new LinkedHashMap<String, Integer>();
216       toPut.put("one", 1);
217       toPut.put("two", 2);
218       toPut.put("three", 3);
219       Map<String, Integer> moreToPut = new LinkedHashMap<String, Integer>();
220       moreToPut.put("four", 4);
221       moreToPut.put("five", 5);
222 
223       ImmutableMap<String, Integer> map = new Builder<String, Integer>()
224           .putAll(toPut)
225           .putAll(moreToPut)
226           .build();
227       assertMapEquals(map,
228           "one", 1, "two", 2, "three", 3, "four", 4, "five", 5);
229     }
230 
231     public void testBuilderReuse() {
232       Builder<String, Integer> builder = new Builder<String, Integer>();
233       ImmutableMap<String, Integer> mapOne = builder
234           .put("one", 1)
235           .put("two", 2)
236           .build();
237       ImmutableMap<String, Integer> mapTwo = builder
238           .put("three", 3)
239           .put("four", 4)
240           .build();
241 
242       assertMapEquals(mapOne, "one", 1, "two", 2);
243       assertMapEquals(mapTwo, "one", 1, "two", 2, "three", 3, "four", 4);
244     }
245     
246     public void testBuilderPutNullKeyFailsAtomically() {
247       Builder<String, Integer> builder = new Builder<String, Integer>();
248       try {
249         builder.put(null, 1);
250         fail();
251       } catch (NullPointerException expected) {}
252       builder.put("foo", 2);
253       assertMapEquals(builder.build(), "foo", 2);
254     }
255     
256     public void testBuilderPutImmutableEntryWithNullKeyFailsAtomically() {
257       Builder<String, Integer> builder = new Builder<String, Integer>();
258       try {
259         builder.put(Maps.immutableEntry((String) null, 1));
260         fail();
261       } catch (NullPointerException expected) {}
262       builder.put("foo", 2);
263       assertMapEquals(builder.build(), "foo", 2);
264     }
265     
266     // for GWT compatibility
267     static class SimpleEntry<K, V> extends AbstractMapEntry<K, V> {
268       public K key;
269       public V value;
270       
271       SimpleEntry(K key, V value) {
272         this.key = key;
273         this.value = value;
274       }
275 
276       @Override
277       public K getKey() {
278         return key;
279       }
280 
281       @Override
282       public V getValue() {
283         return value;
284       }
285     }
286     
287     public void testBuilderPutMutableEntryWithNullKeyFailsAtomically() {
288       Builder<String, Integer> builder = new Builder<String, Integer>();
289       try {
290         builder.put(new SimpleEntry<String, Integer>(null, 1));
291         fail();
292       } catch (NullPointerException expected) {}
293       builder.put("foo", 2);
294       assertMapEquals(builder.build(), "foo", 2);
295     }
296 
297     public void testBuilderPutNullKey() {
298       Builder<String, Integer> builder = new Builder<String, Integer>();
299       try {
300         builder.put(null, 1);
301         fail();
302       } catch (NullPointerException expected) {
303       }
304     }
305 
306     public void testBuilderPutNullValue() {
307       Builder<String, Integer> builder = new Builder<String, Integer>();
308       try {
309         builder.put("one", null);
310         fail();
311       } catch (NullPointerException expected) {
312       }
313     }
314 
315     public void testBuilderPutNullKeyViaPutAll() {
316       Builder<String, Integer> builder = new Builder<String, Integer>();
317       try {
318         builder.putAll(Collections.<String, Integer>singletonMap(null, 1));
319         fail();
320       } catch (NullPointerException expected) {
321       }
322     }
323 
324     public void testBuilderPutNullValueViaPutAll() {
325       Builder<String, Integer> builder = new Builder<String, Integer>();
326       try {
327         builder.putAll(Collections.<String, Integer>singletonMap("one", null));
328         fail();
329       } catch (NullPointerException expected) {
330       }
331     }
332 
333     public void testPuttingTheSameKeyTwiceThrowsOnBuild() {
334       Builder<String, Integer> builder = new Builder<String, Integer>()
335           .put("one", 1)
336           .put("one", 1); // throwing on this line would be even better
337 
338       try {
339         builder.build();
340         fail();
341       } catch (IllegalArgumentException expected) {
342       }
343     }
344 
345     public void testOf() {
346       assertMapEquals(
347           ImmutableMap.of("one", 1),
348           "one", 1);
349       assertMapEquals(
350           ImmutableMap.of("one", 1, "two", 2),
351           "one", 1, "two", 2);
352       assertMapEquals(
353           ImmutableMap.of("one", 1, "two", 2, "three", 3),
354           "one", 1, "two", 2, "three", 3);
355       assertMapEquals(
356           ImmutableMap.of("one", 1, "two", 2, "three", 3, "four", 4),
357           "one", 1, "two", 2, "three", 3, "four", 4);
358       assertMapEquals(
359           ImmutableMap.of("one", 1, "two", 2, "three", 3, "four", 4, "five", 5),
360           "one", 1, "two", 2, "three", 3, "four", 4, "five", 5);
361     }
362 
363     public void testOfNullKey() {
364       try {
365         ImmutableMap.of(null, 1);
366         fail();
367       } catch (NullPointerException expected) {
368       }
369 
370       try {
371         ImmutableMap.of("one", 1, null, 2);
372         fail();
373       } catch (NullPointerException expected) {
374       }
375     }
376 
377     public void testOfNullValue() {
378       try {
379         ImmutableMap.of("one", null);
380         fail();
381       } catch (NullPointerException expected) {
382       }
383 
384       try {
385         ImmutableMap.of("one", 1, "two", null);
386         fail();
387       } catch (NullPointerException expected) {
388       }
389     }
390 
391     public void testOfWithDuplicateKey() {
392       try {
393         ImmutableMap.of("one", 1, "one", 1);
394         fail();
395       } catch (IllegalArgumentException expected) {
396       }
397     }
398     
399     public void testCopyOfEmptyMap() {
400       ImmutableMap<String, Integer> copy
401           = ImmutableMap.copyOf(Collections.<String, Integer>emptyMap());
402       assertEquals(Collections.<String, Integer>emptyMap(), copy);
403       assertSame(copy, ImmutableMap.copyOf(copy));
404     }
405 
406     public void testCopyOfSingletonMap() {
407       ImmutableMap<String, Integer> copy
408           = ImmutableMap.copyOf(Collections.singletonMap("one", 1));
409       assertMapEquals(copy, "one", 1);
410       assertSame(copy, ImmutableMap.copyOf(copy));
411     }
412 
413     public void testCopyOf() {
414       Map<String, Integer> original = new LinkedHashMap<String, Integer>();
415       original.put("one", 1);
416       original.put("two", 2);
417       original.put("three", 3);
418 
419       ImmutableMap<String, Integer> copy = ImmutableMap.copyOf(original);
420       assertMapEquals(copy, "one", 1, "two", 2, "three", 3);
421       assertSame(copy, ImmutableMap.copyOf(copy));
422     }
423   }
424 
425   public void testNullGet() {
426     ImmutableMap<String, Integer> map = ImmutableMap.of("one", 1);
427     assertNull(map.get(null));
428   }
429 
430   public void testAsMultimap() {
431     ImmutableMap<String, Integer> map = ImmutableMap.of(
432         "one", 1, "won", 1, "two", 2, "too", 2, "three", 3);
433     ImmutableSetMultimap<String, Integer> expected = ImmutableSetMultimap.of(
434         "one", 1, "won", 1, "two", 2, "too", 2, "three", 3);
435     assertEquals(expected, map.asMultimap());
436   }
437 
438   public void testAsMultimapWhenEmpty() {
439     ImmutableMap<String, Integer> map = ImmutableMap.of();
440     ImmutableSetMultimap<String, Integer> expected = ImmutableSetMultimap.of();
441     assertEquals(expected, map.asMultimap());
442   }
443 
444   public void testAsMultimapCaches() {
445     ImmutableMap<String, Integer> map = ImmutableMap.of("one", 1);
446     ImmutableSetMultimap<String, Integer> multimap1 = map.asMultimap();
447     ImmutableSetMultimap<String, Integer> multimap2 = map.asMultimap();
448     assertEquals(1, multimap1.asMap().size());
449     assertSame(multimap1, multimap2);
450   }
451 
452   private static <K, V> void assertMapEquals(Map<K, V> map,
453       Object... alternatingKeysAndValues) {
454     assertEquals(map.size(), alternatingKeysAndValues.length / 2);
455     int i = 0;
456     for (Entry<K, V> entry : map.entrySet()) {
457       assertEquals(alternatingKeysAndValues[i++], entry.getKey());
458       assertEquals(alternatingKeysAndValues[i++], entry.getValue());
459     }
460   }
461 
462   private static class IntHolder implements Serializable {
463     public int value;
464 
465     public IntHolder(int value) {
466       this.value = value;
467     }
468 
469     @Override public boolean equals(Object o) {
470       return (o instanceof IntHolder) && ((IntHolder) o).value == value;
471     }
472 
473     @Override public int hashCode() {
474       return value;
475     }
476 
477     private static final long serialVersionUID = 5;
478   }
479 
480   public void testMutableValues() {
481     IntHolder holderA = new IntHolder(1);
482     IntHolder holderB = new IntHolder(2);
483     Map<String, IntHolder> map = ImmutableMap.of("a", holderA, "b", holderB);
484     holderA.value = 3;
485     assertTrue(map.entrySet().contains(
486         Maps.immutableEntry("a", new IntHolder(3))));
487     Map<String, Integer> intMap = ImmutableMap.of("a", 3, "b", 2);
488     assertEquals(intMap.hashCode(), map.entrySet().hashCode());
489     assertEquals(intMap.hashCode(), map.hashCode());
490   }
491 
492   public void testCopyOfEnumMap() {
493     EnumMap<AnEnum, String> map = new EnumMap<AnEnum, String>(AnEnum.class);
494     map.put(AnEnum.B, "foo");
495     map.put(AnEnum.C, "bar");
496     assertTrue(ImmutableMap.copyOf(map) instanceof ImmutableEnumMap);
497   }
498 
499   public void testEquals() {
500     new EqualsTester()
501         .addEqualityGroup(ImmutableList.of(), ImmutableList.of())
502         .addEqualityGroup(ImmutableList.of(1), ImmutableList.of(1))
503         .addEqualityGroup(ImmutableList.of(1, 2), ImmutableList.of(1, 2))
504         .addEqualityGroup(ImmutableList.of(1, 2, 3))
505         .addEqualityGroup(ImmutableList.of(1, 2, 3, 4))
506         .addEqualityGroup(ImmutableList.of(1, 2, 3, 4, 5))
507         .addEqualityGroup(ImmutableList.of(1, 2, 3, 4, 5, 6))
508         .addEqualityGroup(ImmutableList.of(1, 2, 3, 4, 5, 6, 7))
509         .addEqualityGroup(ImmutableList.of(1, 2, 3, 4, 5, 6, 7, 8))
510         .addEqualityGroup(ImmutableList.of(1, 2, 3, 4, 5, 6, 7, 8, 9))
511         .addEqualityGroup(ImmutableList.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10))
512         .addEqualityGroup(ImmutableList.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11))
513         .addEqualityGroup(ImmutableList.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12))
514         .addEqualityGroup(ImmutableList.of(100, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12))
515         .addEqualityGroup(ImmutableList.of(1, 200, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12))
516         .addEqualityGroup(ImmutableList.of(1, 2, 300, 4, 5, 6, 7, 8, 9, 10, 11, 12))
517         .addEqualityGroup(ImmutableList.of(1, 2, 3, 400, 5, 6, 7, 8, 9, 10, 11, 12))
518         .addEqualityGroup(ImmutableList.of(1, 2, 3, 4, 500, 6, 7, 8, 9, 10, 11, 12))
519         .addEqualityGroup(ImmutableList.of(1, 2, 3, 4, 5, 600, 7, 8, 9, 10, 11, 12))
520         .addEqualityGroup(ImmutableList.of(1, 2, 3, 4, 5, 6, 700, 8, 9, 10, 11, 12))
521         .addEqualityGroup(ImmutableList.of(1, 2, 3, 4, 5, 6, 7, 800, 9, 10, 11, 12))
522         .addEqualityGroup(ImmutableList.of(1, 2, 3, 4, 5, 6, 7, 8, 900, 10, 11, 12))
523         .addEqualityGroup(ImmutableList.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 1000, 11, 12))
524         .addEqualityGroup(ImmutableList.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1100, 12))
525         .addEqualityGroup(ImmutableList.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 1200))
526         .testEquals();
527 
528   }
529 }
530