Home | History | Annotate | Download | only in collect
      1 /*
      2  * Copyright (C) 2007 The Guava Authors
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  * http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.google.common.collect;
     18 
     19 import static com.google.common.base.Preconditions.checkArgument;
     20 import static com.google.common.collect.Maps.immutableEntry;
     21 import static com.google.common.collect.Sets.newHashSet;
     22 import static com.google.common.collect.testing.Helpers.nefariousMapEntry;
     23 import static com.google.common.collect.testing.IteratorFeature.MODIFIABLE;
     24 import static com.google.common.truth.Truth.assertThat;
     25 import static java.util.Arrays.asList;
     26 
     27 import com.google.common.annotations.GwtCompatible;
     28 import com.google.common.annotations.GwtIncompatible;
     29 import com.google.common.base.Function;
     30 import com.google.common.base.Functions;
     31 import com.google.common.base.Predicates;
     32 import com.google.common.base.Supplier;
     33 import com.google.common.collect.Maps.EntryTransformer;
     34 import com.google.common.collect.testing.IteratorTester;
     35 import com.google.common.collect.testing.google.UnmodifiableCollectionTests;
     36 import com.google.common.testing.NullPointerTester;
     37 import com.google.common.testing.SerializableTester;
     38 
     39 import junit.framework.TestCase;
     40 
     41 import java.io.Serializable;
     42 import java.util.Arrays;
     43 import java.util.Collection;
     44 import java.util.Collections;
     45 import java.util.Comparator;
     46 import java.util.HashMap;
     47 import java.util.HashSet;
     48 import java.util.Iterator;
     49 import java.util.LinkedList;
     50 import java.util.List;
     51 import java.util.Map;
     52 import java.util.Map.Entry;
     53 import java.util.Queue;
     54 import java.util.RandomAccess;
     55 import java.util.Set;
     56 import java.util.SortedMap;
     57 import java.util.SortedSet;
     58 import java.util.TreeSet;
     59 
     60 import javax.annotation.Nullable;
     61 
     62 /**
     63  * Unit test for {@code Multimaps}.
     64  *
     65  * @author Jared Levy
     66  */
     67 @GwtCompatible(emulated = true)
     68 public class MultimapsTest extends TestCase {
     69 
     70   private static final Comparator<Integer> INT_COMPARATOR =
     71       Ordering.<Integer>natural().reverse().nullsFirst();
     72 
     73   private static final EntryTransformer<Object, Object, Object> ALWAYS_NULL =
     74       new EntryTransformer<Object, Object, Object>() {
     75         @Override
     76         public Object transformEntry(Object k, Object v1) {
     77           return null;
     78         }
     79       };
     80 
     81   @SuppressWarnings("deprecation")
     82   public void testUnmodifiableListMultimapShortCircuit() {
     83     ListMultimap<String, Integer> mod = ArrayListMultimap.create();
     84     ListMultimap<String, Integer> unmod = Multimaps.unmodifiableListMultimap(mod);
     85     assertNotSame(mod, unmod);
     86     assertSame(unmod, Multimaps.unmodifiableListMultimap(unmod));
     87     ImmutableListMultimap<String, Integer> immutable =
     88         ImmutableListMultimap.of("a", 1, "b", 2, "a", 3);
     89     assertSame(immutable, Multimaps.unmodifiableListMultimap(immutable));
     90     assertSame(
     91         immutable, Multimaps.unmodifiableListMultimap((ListMultimap<String, Integer>) immutable));
     92   }
     93 
     94   @SuppressWarnings("deprecation")
     95   public void testUnmodifiableSetMultimapShortCircuit() {
     96     SetMultimap<String, Integer> mod = HashMultimap.create();
     97     SetMultimap<String, Integer> unmod = Multimaps.unmodifiableSetMultimap(mod);
     98     assertNotSame(mod, unmod);
     99     assertSame(unmod, Multimaps.unmodifiableSetMultimap(unmod));
    100     ImmutableSetMultimap<String, Integer> immutable =
    101         ImmutableSetMultimap.of("a", 1, "b", 2, "a", 3);
    102     assertSame(immutable, Multimaps.unmodifiableSetMultimap(immutable));
    103     assertSame(
    104         immutable, Multimaps.unmodifiableSetMultimap((SetMultimap<String, Integer>) immutable));
    105   }
    106 
    107   @SuppressWarnings("deprecation")
    108   public void testUnmodifiableMultimapShortCircuit() {
    109     Multimap<String, Integer> mod = HashMultimap.create();
    110     Multimap<String, Integer> unmod = Multimaps.unmodifiableMultimap(mod);
    111     assertNotSame(mod, unmod);
    112     assertSame(unmod, Multimaps.unmodifiableMultimap(unmod));
    113     ImmutableMultimap<String, Integer> immutable = ImmutableMultimap.of("a", 1, "b", 2, "a", 3);
    114     assertSame(immutable, Multimaps.unmodifiableMultimap(immutable));
    115     assertSame(immutable, Multimaps.unmodifiableMultimap((Multimap<String, Integer>) immutable));
    116   }
    117 
    118   @GwtIncompatible("slow (~10s)")
    119   public void testUnmodifiableArrayListMultimap() {
    120     checkUnmodifiableMultimap(
    121         ArrayListMultimap.<String, Integer>create(), true);
    122   }
    123 
    124   @GwtIncompatible("SerializableTester")
    125   public void testSerializingUnmodifiableArrayListMultimap() {
    126     Multimap<String, Integer> unmodifiable =
    127         prepareUnmodifiableTests(ArrayListMultimap.<String, Integer>create(), true, null, null);
    128     SerializableTester.reserializeAndAssert(unmodifiable);
    129   }
    130 
    131   public void testUnmodifiableArrayListMultimapRandomAccess() {
    132     ListMultimap<String, Integer> delegate = ArrayListMultimap.create();
    133     delegate.put("foo", 1);
    134     delegate.put("foo", 3);
    135     ListMultimap<String, Integer> multimap
    136         = Multimaps.unmodifiableListMultimap(delegate);
    137     assertTrue(multimap.get("foo") instanceof RandomAccess);
    138     assertTrue(multimap.get("bar") instanceof RandomAccess);
    139   }
    140 
    141   public void testUnmodifiableLinkedListMultimapRandomAccess() {
    142     ListMultimap<String, Integer> delegate = LinkedListMultimap.create();
    143     delegate.put("foo", 1);
    144     delegate.put("foo", 3);
    145     ListMultimap<String, Integer> multimap
    146         = Multimaps.unmodifiableListMultimap(delegate);
    147     assertFalse(multimap.get("foo") instanceof RandomAccess);
    148     assertFalse(multimap.get("bar") instanceof RandomAccess);
    149   }
    150 
    151   @GwtIncompatible("slow (~10s)")
    152   public void testUnmodifiableHashMultimap() {
    153     checkUnmodifiableMultimap(HashMultimap.<String, Integer>create(), false);
    154   }
    155 
    156   @GwtIncompatible("SerializableTester")
    157   public void testSerializingUnmodifiableHashMultimap() {
    158     Multimap<String, Integer> unmodifiable =
    159         prepareUnmodifiableTests(HashMultimap.<String, Integer>create(), false, null, null);
    160     SerializableTester.reserializeAndAssert(unmodifiable);
    161   }
    162 
    163   @GwtIncompatible("slow (~10s)")
    164   public void testUnmodifiableTreeMultimap() {
    165     checkUnmodifiableMultimap(
    166         TreeMultimap.<String, Integer>create(), false, "null", 42);
    167   }
    168 
    169   @GwtIncompatible("SerializableTester")
    170   public void testSerializingUnmodifiableTreeMultimap() {
    171     Multimap<String, Integer> unmodifiable =
    172         prepareUnmodifiableTests(TreeMultimap.<String, Integer>create(), false, "null", 42);
    173     SerializableTester.reserializeAndAssert(unmodifiable);
    174   }
    175 
    176   @GwtIncompatible("slow (~10s)")
    177   public void testUnmodifiableSynchronizedArrayListMultimap() {
    178     checkUnmodifiableMultimap(Multimaps.synchronizedListMultimap(
    179         ArrayListMultimap.<String, Integer>create()), true);
    180   }
    181 
    182   @GwtIncompatible("SerializableTester")
    183   public void testSerializingUnmodifiableSynchronizedArrayListMultimap() {
    184     Multimap<String, Integer> unmodifiable =
    185         prepareUnmodifiableTests(Multimaps.synchronizedListMultimap(
    186           ArrayListMultimap.<String, Integer>create()), true, null, null);
    187     SerializableTester.reserializeAndAssert(unmodifiable);
    188   }
    189 
    190   @GwtIncompatible("slow (~10s)")
    191   public void testUnmodifiableSynchronizedHashMultimap() {
    192     checkUnmodifiableMultimap(Multimaps.synchronizedSetMultimap(
    193         HashMultimap.<String, Integer>create()), false);
    194   }
    195 
    196   @GwtIncompatible("SerializableTester")
    197   public void testSerializingUnmodifiableSynchronizedHashMultimap() {
    198     Multimap<String, Integer> unmodifiable =
    199         prepareUnmodifiableTests(Multimaps.synchronizedSetMultimap(
    200         HashMultimap.<String, Integer>create()), false, null, null);
    201     SerializableTester.reserializeAndAssert(unmodifiable);
    202   }
    203 
    204   @GwtIncompatible("slow (~10s)")
    205   public void testUnmodifiableSynchronizedTreeMultimap() {
    206     TreeMultimap<String, Integer> delegate
    207         = TreeMultimap.create(Ordering.<String>natural(), INT_COMPARATOR);
    208     SortedSetMultimap<String, Integer> multimap
    209         = Multimaps.synchronizedSortedSetMultimap(delegate);
    210     checkUnmodifiableMultimap(multimap, false, "null", 42);
    211     assertSame(INT_COMPARATOR, multimap.valueComparator());
    212   }
    213 
    214   @GwtIncompatible("SerializableTester")
    215   public void testSerializingUnmodifiableSynchronizedTreeMultimap() {
    216     TreeMultimap<String, Integer> delegate =
    217         TreeMultimap.create(Ordering.<String>natural(), INT_COMPARATOR);
    218     SortedSetMultimap<String, Integer> multimap =
    219         Multimaps.synchronizedSortedSetMultimap(delegate);
    220     Multimap<String, Integer> unmodifiable =
    221         prepareUnmodifiableTests(multimap, false, "null", 42);
    222     SerializableTester.reserializeAndAssert(unmodifiable);
    223     assertSame(INT_COMPARATOR, multimap.valueComparator());
    224   }
    225 
    226   public void testUnmodifiableMultimapIsView() {
    227     Multimap<String, Integer> mod = HashMultimap.create();
    228     Multimap<String, Integer> unmod = Multimaps.unmodifiableMultimap(mod);
    229     assertEquals(mod, unmod);
    230     mod.put("foo", 1);
    231     assertTrue(unmod.containsEntry("foo", 1));
    232     assertEquals(mod, unmod);
    233   }
    234 
    235   @SuppressWarnings("unchecked")
    236   public void testUnmodifiableMultimapEntries() {
    237     Multimap<String, Integer> mod = HashMultimap.create();
    238     Multimap<String, Integer> unmod = Multimaps.unmodifiableMultimap(mod);
    239     mod.put("foo", 1);
    240     Entry<String, Integer> entry = unmod.entries().iterator().next();
    241     try {
    242       entry.setValue(2);
    243       fail("UnsupportedOperationException expected");
    244     } catch (UnsupportedOperationException expected) {}
    245     entry = (Entry<String, Integer>) unmod.entries().toArray()[0];
    246     try {
    247       entry.setValue(2);
    248       fail("UnsupportedOperationException expected");
    249     } catch (UnsupportedOperationException expected) {}
    250     Entry<String, Integer>[] array
    251         = (Entry<String, Integer>[]) new Entry<?, ?>[2];
    252     assertSame(array, unmod.entries().toArray(array));
    253     try {
    254       array[0].setValue(2);
    255       fail("UnsupportedOperationException expected");
    256     } catch (UnsupportedOperationException expected) {}
    257     assertFalse(unmod.entries().contains(nefariousMapEntry("pwnd", 2)));
    258     assertFalse(unmod.keys().contains("pwnd"));
    259   }
    260 
    261   /**
    262    * The supplied multimap will be mutated and an unmodifiable instance used
    263    * in its stead. The multimap must support null keys and values.
    264    */
    265   private static void checkUnmodifiableMultimap(
    266       Multimap<String, Integer> multimap, boolean permitsDuplicates) {
    267     checkUnmodifiableMultimap(multimap, permitsDuplicates, null, null);
    268   }
    269 
    270   /**
    271    * The supplied multimap will be mutated and an unmodifiable instance used
    272    * in its stead. If the multimap does not support null keys or values,
    273    * alternatives may be specified for tests involving nulls.
    274    */
    275   private static void checkUnmodifiableMultimap(
    276       Multimap<String, Integer> multimap, boolean permitsDuplicates,
    277       @Nullable String nullKey, @Nullable Integer nullValue) {
    278     Multimap<String, Integer> unmodifiable =
    279         prepareUnmodifiableTests(multimap, permitsDuplicates, nullKey, nullValue);
    280 
    281     UnmodifiableCollectionTests.assertMultimapIsUnmodifiable(
    282         unmodifiable, "test", 123);
    283 
    284     assertUnmodifiableIterableInTandem(
    285         unmodifiable.keys(), multimap.keys());
    286 
    287     assertUnmodifiableIterableInTandem(
    288         unmodifiable.keySet(), multimap.keySet());
    289 
    290     assertUnmodifiableIterableInTandem(
    291         unmodifiable.entries(), multimap.entries());
    292 
    293     assertUnmodifiableIterableInTandem(
    294         unmodifiable.asMap().entrySet(), multimap.asMap().entrySet());
    295 
    296     assertEquals(multimap.toString(), unmodifiable.toString());
    297     assertEquals(multimap.hashCode(), unmodifiable.hashCode());
    298     assertEquals(multimap, unmodifiable);
    299 
    300     assertThat(unmodifiable.asMap().get("bar")).has().exactly(5, -1);
    301     assertNull(unmodifiable.asMap().get("missing"));
    302 
    303     assertFalse(unmodifiable.entries() instanceof Serializable);
    304   }
    305 
    306   /**
    307    * Prepares the multimap for unmodifiable tests, returning an unmodifiable view
    308    * of the map.
    309    */
    310   private static Multimap<String, Integer> prepareUnmodifiableTests(
    311       Multimap<String, Integer> multimap, boolean permitsDuplicates,
    312       @Nullable String nullKey, @Nullable Integer nullValue) {
    313     multimap.clear();
    314     multimap.put("foo", 1);
    315     multimap.put("foo", 2);
    316     multimap.put("foo", 3);
    317     multimap.put("bar", 5);
    318     multimap.put("bar", -1);
    319     multimap.put(nullKey, nullValue);
    320     multimap.put("foo", nullValue);
    321     multimap.put(nullKey, 5);
    322     multimap.put("foo", 2);
    323 
    324     if (permitsDuplicates) {
    325       assertEquals(9, multimap.size());
    326     } else {
    327       assertEquals(8, multimap.size());
    328     }
    329 
    330     Multimap<String, Integer> unmodifiable;
    331     if (multimap instanceof SortedSetMultimap) {
    332       unmodifiable = Multimaps.unmodifiableSortedSetMultimap(
    333           (SortedSetMultimap<String, Integer>) multimap);
    334     } else if (multimap instanceof SetMultimap) {
    335       unmodifiable = Multimaps.unmodifiableSetMultimap(
    336           (SetMultimap<String, Integer>) multimap);
    337     } else if (multimap instanceof ListMultimap) {
    338       unmodifiable = Multimaps.unmodifiableListMultimap(
    339           (ListMultimap<String, Integer>) multimap);
    340     } else {
    341       unmodifiable = Multimaps.unmodifiableMultimap(multimap);
    342     }
    343     return unmodifiable;
    344   }
    345 
    346   private static <T> void assertUnmodifiableIterableInTandem(
    347       Iterable<T> unmodifiable, Iterable<T> modifiable) {
    348     UnmodifiableCollectionTests.assertIteratorIsUnmodifiable(
    349         unmodifiable.iterator());
    350     UnmodifiableCollectionTests.assertIteratorsInOrder(
    351         unmodifiable.iterator(), modifiable.iterator());
    352   }
    353 
    354   public void testInvertFrom() {
    355     ImmutableMultimap<Integer, String> empty = ImmutableMultimap.of();
    356 
    357     // typical usage example - sad that ArrayListMultimap.create() won't work
    358     Multimap<String, Integer> multimap = Multimaps.invertFrom(empty,
    359         ArrayListMultimap.<String, Integer>create());
    360     assertTrue(multimap.isEmpty());
    361 
    362     ImmutableMultimap<Integer, String> single
    363         = new ImmutableMultimap.Builder<Integer, String>()
    364             .put(1, "one")
    365             .put(2, "two")
    366             .build();
    367 
    368     // copy into existing multimap
    369     assertSame(multimap, Multimaps.invertFrom(single, multimap));
    370 
    371     ImmutableMultimap<String, Integer> expected
    372         = new ImmutableMultimap.Builder<String, Integer>()
    373         .put("one", 1)
    374         .put("two", 2)
    375         .build();
    376 
    377     assertEquals(expected, multimap);
    378   }
    379 
    380   public void testAsMap_multimap() {
    381     Multimap<String, Integer> multimap = Multimaps.newMultimap(
    382         new HashMap<String, Collection<Integer>>(), new QueueSupplier());
    383     Map<String, Collection<Integer>> map = Multimaps.asMap(multimap);
    384     assertSame(multimap.asMap(), map);
    385   }
    386 
    387   public void testAsMap_listMultimap() {
    388     ListMultimap<String, Integer> listMultimap = ArrayListMultimap.create();
    389     Map<String, List<Integer>> map = Multimaps.asMap(listMultimap);
    390     assertSame(listMultimap.asMap(), map);
    391   }
    392 
    393   public void testAsMap_setMultimap() {
    394     SetMultimap<String, Integer> setMultimap = LinkedHashMultimap.create();
    395     Map<String, Set<Integer>> map = Multimaps.asMap(setMultimap);
    396     assertSame(setMultimap.asMap(), map);
    397   }
    398 
    399   public void testAsMap_sortedSetMultimap() {
    400     SortedSetMultimap<String, Integer> sortedSetMultimap =
    401         TreeMultimap.create();
    402     Map<String, SortedSet<Integer>> map = Multimaps.asMap(sortedSetMultimap);
    403     assertSame(sortedSetMultimap.asMap(), map);
    404   }
    405 
    406   public void testForMap() {
    407     Map<String, Integer> map = Maps.newHashMap();
    408     map.put("foo", 1);
    409     map.put("bar", 2);
    410     Multimap<String, Integer> multimap = HashMultimap.create();
    411     multimap.put("foo", 1);
    412     multimap.put("bar", 2);
    413     Multimap<String, Integer> multimapView = Multimaps.forMap(map);
    414     assertTrue(multimap.equals(multimapView));
    415     assertTrue(multimapView.equals(multimap));
    416     assertTrue(multimapView.equals(multimapView));
    417     assertFalse(multimapView.equals(map));
    418     Multimap<String, Integer> multimap2 = HashMultimap.create();
    419     multimap2.put("foo", 1);
    420     assertFalse(multimapView.equals(multimap2));
    421     multimap2.put("bar", 1);
    422     assertFalse(multimapView.equals(multimap2));
    423     ListMultimap<String, Integer> listMultimap
    424         = new ImmutableListMultimap.Builder<String, Integer>()
    425             .put("foo", 1).put("bar", 2).build();
    426     assertFalse("SetMultimap equals ListMultimap",
    427         multimapView.equals(listMultimap));
    428     assertEquals(multimap.toString(), multimapView.toString());
    429     assertEquals(multimap.hashCode(), multimapView.hashCode());
    430     assertEquals(multimap.size(), multimapView.size());
    431     assertTrue(multimapView.containsKey("foo"));
    432     assertTrue(multimapView.containsValue(1));
    433     assertTrue(multimapView.containsEntry("bar", 2));
    434     assertEquals(Collections.singleton(1), multimapView.get("foo"));
    435     assertEquals(Collections.singleton(2), multimapView.get("bar"));
    436     try {
    437       multimapView.put("baz", 3);
    438       fail("UnsupportedOperationException expected");
    439     } catch (UnsupportedOperationException expected) {}
    440     try {
    441       multimapView.putAll("baz", Collections.singleton(3));
    442       fail("UnsupportedOperationException expected");
    443     } catch (UnsupportedOperationException expected) {}
    444     try {
    445       multimapView.putAll(multimap);
    446       fail("UnsupportedOperationException expected");
    447     } catch (UnsupportedOperationException expected) {}
    448     try {
    449       multimapView.replaceValues("foo", Collections.<Integer>emptySet());
    450       fail("UnsupportedOperationException expected");
    451     } catch (UnsupportedOperationException expected) {}
    452     multimapView.remove("bar", 2);
    453     assertFalse(multimapView.containsKey("bar"));
    454     assertFalse(map.containsKey("bar"));
    455     assertEquals(map.keySet(), multimapView.keySet());
    456     assertEquals(map.keySet(), multimapView.keys().elementSet());
    457     assertThat(multimapView.keys()).has().item("foo");
    458     assertThat(multimapView.values()).has().item(1);
    459     assertThat(multimapView.entries()).has().item(
    460         Maps.immutableEntry("foo", 1));
    461     assertThat(multimapView.asMap().entrySet()).has().item(
    462         Maps.immutableEntry(
    463             "foo", (Collection<Integer>) Collections.singleton(1)));
    464     multimapView.clear();
    465     assertFalse(multimapView.containsKey("foo"));
    466     assertFalse(map.containsKey("foo"));
    467     assertTrue(map.isEmpty());
    468     assertTrue(multimapView.isEmpty());
    469     multimap.clear();
    470     assertEquals(multimap.toString(), multimapView.toString());
    471     assertEquals(multimap.hashCode(), multimapView.hashCode());
    472     assertEquals(multimap.size(), multimapView.size());
    473     assertEquals(multimapView, ArrayListMultimap.create());
    474   }
    475 
    476   @GwtIncompatible("SerializableTester")
    477   public void testForMapSerialization() {
    478     Map<String, Integer> map = Maps.newHashMap();
    479     map.put("foo", 1);
    480     map.put("bar", 2);
    481     Multimap<String, Integer> multimapView = Multimaps.forMap(map);
    482     SerializableTester.reserializeAndAssert(multimapView);
    483   }
    484 
    485   public void testForMapRemoveAll() {
    486     Map<String, Integer> map = Maps.newHashMap();
    487     map.put("foo", 1);
    488     map.put("bar", 2);
    489     map.put("cow", 3);
    490     Multimap<String, Integer> multimap = Multimaps.forMap(map);
    491     assertEquals(3, multimap.size());
    492     assertEquals(Collections.emptySet(), multimap.removeAll("dog"));
    493     assertEquals(3, multimap.size());
    494     assertTrue(multimap.containsKey("bar"));
    495     assertEquals(Collections.singleton(2), multimap.removeAll("bar"));
    496     assertEquals(2, multimap.size());
    497     assertFalse(multimap.containsKey("bar"));
    498   }
    499 
    500   public void testForMapAsMap() {
    501     Map<String, Integer> map = Maps.newHashMap();
    502     map.put("foo", 1);
    503     map.put("bar", 2);
    504     Map<String, Collection<Integer>> asMap = Multimaps.forMap(map).asMap();
    505     assertEquals(Collections.singleton(1), asMap.get("foo"));
    506     assertNull(asMap.get("cow"));
    507     assertTrue(asMap.containsKey("foo"));
    508     assertFalse(asMap.containsKey("cow"));
    509 
    510     Set<Entry<String, Collection<Integer>>> entries = asMap.entrySet();
    511     assertFalse(entries.contains(4.5));
    512     assertFalse(entries.remove(4.5));
    513     assertFalse(entries.contains(Maps.immutableEntry("foo",
    514         Collections.singletonList(1))));
    515     assertFalse(entries.remove(Maps.immutableEntry("foo",
    516         Collections.singletonList(1))));
    517     assertFalse(entries.contains(Maps.immutableEntry("foo",
    518         Sets.newLinkedHashSet(asList(1, 2)))));
    519     assertFalse(entries.remove(Maps.immutableEntry("foo",
    520         Sets.newLinkedHashSet(asList(1, 2)))));
    521     assertFalse(entries.contains(Maps.immutableEntry("foo",
    522         Collections.singleton(2))));
    523     assertFalse(entries.remove(Maps.immutableEntry("foo",
    524         Collections.singleton(2))));
    525     assertTrue(map.containsKey("foo"));
    526     assertTrue(entries.contains(Maps.immutableEntry("foo",
    527         Collections.singleton(1))));
    528     assertTrue(entries.remove(Maps.immutableEntry("foo",
    529         Collections.singleton(1))));
    530     assertFalse(map.containsKey("foo"));
    531   }
    532 
    533   public void testForMapGetIteration() {
    534     IteratorTester<Integer> tester =
    535         new IteratorTester<Integer>(4, MODIFIABLE, newHashSet(1),
    536             IteratorTester.KnownOrder.KNOWN_ORDER) {
    537           private Multimap<String, Integer> multimap;
    538 
    539           @Override protected Iterator<Integer> newTargetIterator() {
    540             Map<String, Integer> map = Maps.newHashMap();
    541             map.put("foo", 1);
    542             map.put("bar", 2);
    543             multimap = Multimaps.forMap(map);
    544             return multimap.get("foo").iterator();
    545           }
    546 
    547           @Override protected void verify(List<Integer> elements) {
    548             assertEquals(newHashSet(elements), multimap.get("foo"));
    549           }
    550         };
    551 
    552     tester.test();
    553   }
    554 
    555   private enum Color {BLUE, RED, YELLOW, GREEN}
    556 
    557   private abstract static class CountingSupplier<E>
    558       implements Supplier<E>, Serializable {
    559     int count;
    560 
    561     abstract E getImpl();
    562 
    563     @Override
    564     public E get() {
    565       count++;
    566       return getImpl();
    567     }
    568   }
    569 
    570   private static class QueueSupplier extends CountingSupplier<Queue<Integer>> {
    571     @Override public Queue<Integer> getImpl() {
    572       return new LinkedList<Integer>();
    573     }
    574     private static final long serialVersionUID = 0;
    575   }
    576 
    577   public void testNewMultimapWithCollectionRejectingNegativeElements() {
    578     CountingSupplier<Set<Integer>> factory = new SetSupplier() {
    579       @Override
    580       public Set<Integer> getImpl() {
    581         final Set<Integer> backing = super.getImpl();
    582         return new ForwardingSet<Integer>() {
    583           @Override
    584           protected Set<Integer> delegate() {
    585             return backing;
    586           }
    587 
    588           @Override
    589           public boolean add(Integer element) {
    590             checkArgument(element >= 0);
    591             return super.add(element);
    592           }
    593 
    594           @Override
    595           public boolean addAll(Collection<? extends Integer> collection) {
    596             return standardAddAll(collection);
    597           }
    598         };
    599       }
    600     };
    601 
    602     Map<Color, Collection<Integer>> map = Maps.newEnumMap(Color.class);
    603     Multimap<Color, Integer> multimap = Multimaps.newMultimap(map, factory);
    604     try {
    605       multimap.put(Color.BLUE, -1);
    606       fail("Expected IllegalArgumentException");
    607     } catch (IllegalArgumentException expected) {
    608       // expected
    609     }
    610     multimap.put(Color.RED, 1);
    611     multimap.put(Color.BLUE, 2);
    612     try {
    613       multimap.put(Color.GREEN, -1);
    614       fail("Expected IllegalArgumentException");
    615     } catch (IllegalArgumentException expected) {
    616       // expected
    617     }
    618     assertThat(multimap.entries()).has().exactly(
    619         Maps.immutableEntry(Color.RED, 1),
    620         Maps.immutableEntry(Color.BLUE, 2));
    621   }
    622 
    623   public void testNewMultimap() {
    624     // The ubiquitous EnumArrayBlockingQueueMultimap
    625     CountingSupplier<Queue<Integer>> factory = new QueueSupplier();
    626 
    627     Map<Color, Collection<Integer>> map = Maps.newEnumMap(Color.class);
    628     Multimap<Color, Integer> multimap = Multimaps.newMultimap(map, factory);
    629     assertEquals(0, factory.count);
    630     multimap.putAll(Color.BLUE, asList(3, 1, 4));
    631     assertEquals(1, factory.count);
    632     multimap.putAll(Color.RED, asList(2, 7, 1, 8));
    633     assertEquals(2, factory.count);
    634     assertEquals("[3, 1, 4]", multimap.get(Color.BLUE).toString());
    635 
    636     Multimap<Color, Integer> ummodifiable =
    637         Multimaps.unmodifiableMultimap(multimap);
    638     assertEquals("[3, 1, 4]", ummodifiable.get(Color.BLUE).toString());
    639 
    640     Collection<Integer> collection = multimap.get(Color.BLUE);
    641     assertEquals(collection, collection);
    642 
    643     assertFalse(multimap.keySet() instanceof SortedSet);
    644     assertFalse(multimap.asMap() instanceof SortedMap);
    645   }
    646 
    647   @GwtIncompatible("SerializableTester")
    648   public void testNewMultimapSerialization() {
    649     CountingSupplier<Queue<Integer>> factory = new QueueSupplier();
    650     Map<Color, Collection<Integer>> map = Maps.newEnumMap(Color.class);
    651     Multimap<Color, Integer> multimap = Multimaps.newMultimap(map, factory);
    652     multimap.putAll(Color.BLUE, asList(3, 1, 4));
    653     multimap.putAll(Color.RED, asList(2, 7, 1, 8));
    654     SerializableTester.reserializeAndAssert(multimap);
    655   }
    656 
    657   private static class ListSupplier extends
    658       CountingSupplier<LinkedList<Integer>> {
    659     @Override public LinkedList<Integer> getImpl() {
    660       return new LinkedList<Integer>();
    661     }
    662     private static final long serialVersionUID = 0;
    663   }
    664 
    665   public void testNewListMultimap() {
    666     CountingSupplier<LinkedList<Integer>> factory = new ListSupplier();
    667     Map<Color, Collection<Integer>> map = Maps.newTreeMap();
    668     ListMultimap<Color, Integer> multimap =
    669         Multimaps.newListMultimap(map, factory);
    670     assertEquals(0, factory.count);
    671     multimap.putAll(Color.BLUE, asList(3, 1, 4, 1));
    672     assertEquals(1, factory.count);
    673     multimap.putAll(Color.RED, asList(2, 7, 1, 8));
    674     assertEquals(2, factory.count);
    675     assertEquals("{BLUE=[3, 1, 4, 1], RED=[2, 7, 1, 8]}", multimap.toString());
    676     assertFalse(multimap.get(Color.BLUE) instanceof RandomAccess);
    677 
    678     assertTrue(multimap.keySet() instanceof SortedSet);
    679     assertTrue(multimap.asMap() instanceof SortedMap);
    680   }
    681 
    682   @GwtIncompatible("SerializableTester")
    683   public void testNewListMultimapSerialization() {
    684     CountingSupplier<LinkedList<Integer>> factory = new ListSupplier();
    685     Map<Color, Collection<Integer>> map = Maps.newTreeMap();
    686     ListMultimap<Color, Integer> multimap = Multimaps.newListMultimap(map, factory);
    687     multimap.putAll(Color.BLUE, asList(3, 1, 4, 1));
    688     multimap.putAll(Color.RED, asList(2, 7, 1, 8));
    689     SerializableTester.reserializeAndAssert(multimap);
    690   }
    691 
    692   private static class SetSupplier extends CountingSupplier<Set<Integer>> {
    693     @Override public Set<Integer> getImpl() {
    694       return new HashSet<Integer>(4);
    695     }
    696     private static final long serialVersionUID = 0;
    697   }
    698 
    699   public void testNewSetMultimap() {
    700     CountingSupplier<Set<Integer>> factory = new SetSupplier();
    701     Map<Color, Collection<Integer>> map = Maps.newHashMap();
    702     SetMultimap<Color, Integer> multimap =
    703         Multimaps.newSetMultimap(map, factory);
    704     assertEquals(0, factory.count);
    705     multimap.putAll(Color.BLUE, asList(3, 1, 4));
    706     assertEquals(1, factory.count);
    707     multimap.putAll(Color.RED, asList(2, 7, 1, 8));
    708     assertEquals(2, factory.count);
    709     assertEquals(Sets.newHashSet(4, 3, 1), multimap.get(Color.BLUE));
    710   }
    711 
    712   @GwtIncompatible("SerializableTester")
    713   public void testNewSetMultimapSerialization() {
    714     CountingSupplier<Set<Integer>> factory = new SetSupplier();
    715     Map<Color, Collection<Integer>> map = Maps.newHashMap();
    716     SetMultimap<Color, Integer> multimap = Multimaps.newSetMultimap(map, factory);
    717     multimap.putAll(Color.BLUE, asList(3, 1, 4));
    718     multimap.putAll(Color.RED, asList(2, 7, 1, 8));
    719     SerializableTester.reserializeAndAssert(multimap);
    720   }
    721 
    722   private static class SortedSetSupplier extends
    723       CountingSupplier<TreeSet<Integer>> {
    724     @Override public TreeSet<Integer> getImpl() {
    725       return Sets.newTreeSet(INT_COMPARATOR);
    726     }
    727     private static final long serialVersionUID = 0;
    728   }
    729 
    730   public void testNewSortedSetMultimap() {
    731     CountingSupplier<TreeSet<Integer>> factory = new SortedSetSupplier();
    732     Map<Color, Collection<Integer>> map = Maps.newEnumMap(Color.class);
    733     SortedSetMultimap<Color, Integer> multimap =
    734         Multimaps.newSortedSetMultimap(map, factory);
    735     // newSortedSetMultimap calls the factory once to determine the comparator.
    736     assertEquals(1, factory.count);
    737     multimap.putAll(Color.BLUE, asList(3, 1, 4));
    738     assertEquals(2, factory.count);
    739     multimap.putAll(Color.RED, asList(2, 7, 1, 8));
    740     assertEquals(3, factory.count);
    741     assertEquals("[4, 3, 1]", multimap.get(Color.BLUE).toString());
    742     assertEquals(INT_COMPARATOR, multimap.valueComparator());
    743   }
    744 
    745   @GwtIncompatible("SerializableTester")
    746   public void testNewSortedSetMultimapSerialization() {
    747     CountingSupplier<TreeSet<Integer>> factory = new SortedSetSupplier();
    748     Map<Color, Collection<Integer>> map = Maps.newEnumMap(Color.class);
    749     SortedSetMultimap<Color, Integer> multimap = Multimaps.newSortedSetMultimap(map, factory);
    750     multimap.putAll(Color.BLUE, asList(3, 1, 4));
    751     multimap.putAll(Color.RED, asList(2, 7, 1, 8));
    752     SerializableTester.reserializeAndAssert(multimap);
    753     assertEquals(INT_COMPARATOR, multimap.valueComparator());
    754   }
    755 
    756   public void testIndex() {
    757     final Multimap<String, Object> stringToObject =
    758         new ImmutableMultimap.Builder<String, Object>()
    759             .put("1", 1)
    760             .put("1", 1L)
    761             .put("1", "1")
    762             .put("2", 2)
    763             .put("2", 2L)
    764             .build();
    765 
    766     ImmutableMultimap<String, Object> outputMap =
    767         Multimaps.index(stringToObject.values(),
    768             Functions.toStringFunction());
    769     assertEquals(stringToObject, outputMap);
    770   }
    771 
    772   public void testIndexIterator() {
    773     final Multimap<String, Object> stringToObject =
    774         new ImmutableMultimap.Builder<String, Object>()
    775             .put("1", 1)
    776             .put("1", 1L)
    777             .put("1", "1")
    778             .put("2", 2)
    779             .put("2", 2L)
    780             .build();
    781 
    782     ImmutableMultimap<String, Object> outputMap =
    783         Multimaps.index(stringToObject.values().iterator(),
    784             Functions.toStringFunction());
    785     assertEquals(stringToObject, outputMap);
    786   }
    787 
    788   public void testIndex_ordering() {
    789     final Multimap<Integer, String> expectedIndex =
    790         new ImmutableListMultimap.Builder<Integer, String>()
    791             .put(4, "Inky")
    792             .put(6, "Blinky")
    793             .put(5, "Pinky")
    794             .put(5, "Pinky")
    795             .put(5, "Clyde")
    796             .build();
    797 
    798     final List<String> badGuys =
    799         Arrays.asList("Inky", "Blinky", "Pinky", "Pinky", "Clyde");
    800     final Function<String, Integer> stringLengthFunction =
    801         new Function<String, Integer>() {
    802           @Override
    803           public Integer apply(String input) {
    804             return input.length();
    805           }
    806         };
    807 
    808     Multimap<Integer, String> index =
    809         Multimaps.index(badGuys, stringLengthFunction);
    810 
    811     assertEquals(expectedIndex, index);
    812   }
    813 
    814   public void testIndex_nullValue() {
    815     List<Integer> values = Arrays.asList(1, null);
    816     try {
    817       Multimaps.index(values, Functions.identity());
    818       fail();
    819     } catch (NullPointerException e) {}
    820   }
    821 
    822   public void testIndex_nullKey() {
    823     List<Integer> values = Arrays.asList(1, 2);
    824     try {
    825       Multimaps.index(values, Functions.constant(null));
    826       fail();
    827     } catch (NullPointerException e) {}
    828   }
    829 
    830   @GwtIncompatible(value = "untested")
    831   public void testTransformValues() {
    832     SetMultimap<String, Integer> multimap =
    833         ImmutableSetMultimap.of("a", 2, "b", -3, "b", 3, "a", 4, "c", 6);
    834     Function<Integer, Integer> square = new Function<Integer, Integer>() {
    835       @Override
    836       public Integer apply(Integer in) {
    837         return in * in;
    838       }
    839     };
    840     Multimap<String, Integer> transformed = Multimaps.transformValues(multimap, square);
    841     assertThat(transformed.entries()).has().exactly(immutableEntry("a", 4),
    842         immutableEntry("a", 16), immutableEntry("b", 9), immutableEntry("b", 9),
    843         immutableEntry("c", 36)).inOrder();
    844   }
    845 
    846   @GwtIncompatible(value = "untested")
    847   public void testTransformValuesIsView() {
    848     Multimap<String, String> multimap = LinkedListMultimap.create();
    849     multimap.put("a", "a");
    850     Multimap<String, Integer> transformed =
    851         Multimaps.transformValues(multimap, new Function<String, Integer>() {
    852 
    853           @Override public Integer apply(String str) {
    854             return str.length();
    855           }
    856         });
    857     Entry<String, String> entry = multimap.entries().iterator().next();
    858     entry.setValue("bbb");
    859     assertThat(transformed.entries()).has().exactly(immutableEntry("a", 3)).inOrder();
    860   }
    861 
    862   @GwtIncompatible(value = "untested")
    863   public void testTransformListValues() {
    864     ListMultimap<String, Integer> multimap =
    865         ImmutableListMultimap.of("a", 2, "b", -3, "b", 3, "a", 4, "c", 6);
    866     Function<Integer, Integer> square = new Function<Integer, Integer>() {
    867       @Override
    868       public Integer apply(Integer in) {
    869         return in * in;
    870       }
    871     };
    872     ListMultimap<String, Integer> transformed =
    873         Multimaps.transformValues(multimap, square);
    874     assertThat(transformed.entries()).has().exactly(immutableEntry("a", 4),
    875         immutableEntry("a", 16), immutableEntry("b", 9), immutableEntry("b", 9),
    876         immutableEntry("c", 36)).inOrder();
    877   }
    878 
    879   @GwtIncompatible(value = "untested")
    880   public void testTransformEntries() {
    881     SetMultimap<String, Integer> multimap =
    882         ImmutableSetMultimap.of("a", 1, "a", 4, "b", -6);
    883     EntryTransformer<String, Integer, String> transformer =
    884         new EntryTransformer<String, Integer, String>() {
    885           @Override
    886           public String transformEntry(String key, Integer value) {
    887             return (value >= 0) ? key : "no" + key;
    888           }
    889         };
    890     Multimap<String, String> transformed =
    891         Multimaps.transformEntries(multimap, transformer);
    892     assertThat(transformed.entries()).has().exactly(immutableEntry("a", "a"),
    893         immutableEntry("a", "a"), immutableEntry("b", "nob")).inOrder();
    894   }
    895 
    896   @GwtIncompatible(value = "untested")
    897   public void testTransformListEntries() {
    898     ListMultimap<String, Integer> multimap =
    899         ImmutableListMultimap.of("a", 1, "a", 4, "b", 6, "a", 4);
    900     EntryTransformer<String, Integer, String> transformer =
    901         new EntryTransformer<String, Integer, String>() {
    902           @Override
    903           public String transformEntry(String key, Integer value) {
    904             return key + value;
    905           }
    906         };
    907     ListMultimap<String, String> transformed =
    908         Multimaps.transformEntries(multimap, transformer);
    909     assertEquals(
    910         ImmutableListMultimap.of("a", "a1", "a", "a4", "a", "a4", "b", "b6"),
    911         transformed);
    912     assertEquals("{a=[a1, a4, a4], b=[b6]}", transformed.toString());
    913   }
    914 
    915   public <K, V> void testSynchronizedMultimapSampleCodeCompilation() {
    916     K key = null;
    917 
    918     Multimap<K, V> multimap = Multimaps.synchronizedMultimap(
    919         HashMultimap.<K, V>create());
    920     Collection<V> values = multimap.get(key);  // Needn't be in synchronized block
    921     synchronized (multimap) {  // Synchronizing on multimap, not values!
    922       Iterator<V> i = values.iterator(); // Must be in synchronized block
    923       while (i.hasNext()) {
    924         foo(i.next());
    925       }
    926     }
    927   }
    928 
    929   private static void foo(Object o) {}
    930 
    931   public void testFilteredKeysSetMultimapReplaceValues() {
    932     SetMultimap<String, Integer> multimap = LinkedHashMultimap.create();
    933     multimap.put("foo", 1);
    934     multimap.put("bar", 2);
    935     multimap.put("baz", 3);
    936     multimap.put("bar", 4);
    937 
    938     SetMultimap<String, Integer> filtered = Multimaps.filterKeys(
    939         multimap, Predicates.in(ImmutableSet.of("foo", "bar")));
    940 
    941     assertEquals(
    942         ImmutableSet.of(),
    943         filtered.replaceValues("baz", ImmutableSet.<Integer>of()));
    944 
    945     try {
    946       filtered.replaceValues("baz", ImmutableSet.of(5));
    947       fail("Expected IllegalArgumentException");
    948     } catch (IllegalArgumentException expected) {
    949     }
    950   }
    951 
    952   public void testFilteredKeysSetMultimapGetBadValue() {
    953     SetMultimap<String, Integer> multimap = LinkedHashMultimap.create();
    954     multimap.put("foo", 1);
    955     multimap.put("bar", 2);
    956     multimap.put("baz", 3);
    957     multimap.put("bar", 4);
    958 
    959     SetMultimap<String, Integer> filtered = Multimaps.filterKeys(
    960         multimap, Predicates.in(ImmutableSet.of("foo", "bar")));
    961     Set<Integer> bazSet = filtered.get("baz");
    962     assertThat(bazSet).isEmpty();
    963     try {
    964       bazSet.add(5);
    965       fail("Expected IllegalArgumentException");
    966     } catch (IllegalArgumentException expected) {
    967     }
    968     try {
    969       bazSet.addAll(ImmutableSet.of(6, 7));
    970       fail("Expected IllegalArgumentException");
    971     } catch (IllegalArgumentException expected) {
    972     }
    973   }
    974 
    975   public void testFilteredKeysListMultimapGetBadValue() {
    976     ListMultimap<String, Integer> multimap = ArrayListMultimap.create();
    977     multimap.put("foo", 1);
    978     multimap.put("bar", 2);
    979     multimap.put("baz", 3);
    980     multimap.put("bar", 4);
    981 
    982     ListMultimap<String, Integer> filtered = Multimaps.filterKeys(
    983         multimap, Predicates.in(ImmutableSet.of("foo", "bar")));
    984     List<Integer> bazList = filtered.get("baz");
    985     assertThat(bazList).isEmpty();
    986     try {
    987       bazList.add(5);
    988       fail("Expected IllegalArgumentException");
    989     } catch (IllegalArgumentException expected) {
    990     }
    991     try {
    992       bazList.add(0, 6);
    993       fail("Expected IllegalArgumentException");
    994     } catch (IllegalArgumentException expected) {
    995     }
    996     try {
    997       bazList.addAll(ImmutableList.of(7, 8));
    998       fail("Expected IllegalArgumentException");
    999     } catch (IllegalArgumentException expected) {
   1000     }
   1001     try {
   1002       bazList.addAll(0, ImmutableList.of(9, 10));
   1003       fail("Expected IllegalArgumentException");
   1004     } catch (IllegalArgumentException expected) {
   1005     }
   1006   }
   1007 
   1008   @GwtIncompatible("NullPointerTester")
   1009   public void testNullPointers() {
   1010     new NullPointerTester().testAllPublicStaticMethods(Multimaps.class);
   1011   }
   1012 }
   1013