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