View Javadoc
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 }