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