MultimapsTest.java revision 1d580d0f6ee4f21eb309ba7b509d2c6d671c4044
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
17package com.google.common.collect;
18
19import static com.google.common.collect.Maps.immutableEntry;
20import static com.google.common.collect.Sets.newHashSet;
21import static com.google.common.collect.testing.Helpers.nefariousMapEntry;
22import static com.google.common.collect.testing.IteratorFeature.MODIFIABLE;
23import static java.util.Arrays.asList;
24import static org.junit.contrib.truth.Truth.ASSERT;
25
26import com.google.common.annotations.GwtCompatible;
27import com.google.common.annotations.GwtIncompatible;
28import com.google.common.base.Function;
29import com.google.common.base.Functions;
30import com.google.common.base.Supplier;
31import com.google.common.collect.Maps.EntryTransformer;
32import com.google.common.collect.testing.IteratorTester;
33import com.google.common.collect.testing.google.UnmodifiableCollectionTests;
34import com.google.common.testing.NullPointerTester;
35import com.google.common.testing.SerializableTester;
36
37import java.io.Serializable;
38import java.util.Arrays;
39import java.util.Collection;
40import java.util.Collections;
41import java.util.Comparator;
42import java.util.HashSet;
43import java.util.Iterator;
44import java.util.LinkedList;
45import java.util.List;
46import java.util.Map;
47import java.util.Map.Entry;
48import java.util.Queue;
49import java.util.RandomAccess;
50import java.util.Set;
51import java.util.SortedMap;
52import java.util.SortedSet;
53import java.util.TreeSet;
54
55import javax.annotation.Nullable;
56
57/**
58 * Unit test for {@code Multimaps}.
59 *
60 * @author Jared Levy
61 */
62@GwtCompatible(emulated = true)
63public class MultimapsTest extends AbstractMultimapTest {
64  private static final Comparator<Integer> INT_COMPARATOR =
65      Ordering.<Integer>natural().reverse().nullsFirst();
66
67  private static final EntryTransformer<Object, Object, Object> ALWAYS_NULL =
68      new EntryTransformer<Object, Object, Object>() {
69        @Override
70        public Object transformEntry(Object k, Object v1) {
71          return null;
72        }
73      };
74
75  @Override protected Multimap<String, Integer> create() {
76    return Multimaps.synchronizedSetMultimap(
77        HashMultimap.<String, Integer>create());
78  }
79
80  @SuppressWarnings("deprecation")
81  public void testUnmodifiableListMultimapShortCircuit(){
82    ListMultimap<String, Integer> mod = ArrayListMultimap.create();
83    ListMultimap<String, Integer> unmod = Multimaps.unmodifiableListMultimap(mod);
84    assertNotSame(mod, unmod);
85    assertSame(unmod, Multimaps.unmodifiableListMultimap(unmod));
86    ImmutableListMultimap<String, Integer> immutable =
87        ImmutableListMultimap.of("a", 1, "b", 2, "a", 3);
88    assertSame(immutable, Multimaps.unmodifiableListMultimap(immutable));
89    assertSame(
90        immutable, Multimaps.unmodifiableListMultimap((ListMultimap<String, Integer>) immutable));
91  }
92
93  @SuppressWarnings("deprecation")
94  public void testUnmodifiableSetMultimapShortCircuit(){
95    SetMultimap<String, Integer> mod = HashMultimap.create();
96    SetMultimap<String, Integer> unmod = Multimaps.unmodifiableSetMultimap(mod);
97    assertNotSame(mod, unmod);
98    assertSame(unmod, Multimaps.unmodifiableSetMultimap(unmod));
99    ImmutableSetMultimap<String, Integer> immutable =
100        ImmutableSetMultimap.of("a", 1, "b", 2, "a", 3);
101    assertSame(immutable, Multimaps.unmodifiableSetMultimap(immutable));
102    assertSame(
103        immutable, Multimaps.unmodifiableSetMultimap((SetMultimap<String, Integer>) immutable));
104  }
105
106  @SuppressWarnings("deprecation")
107  public void testUnmodifiableMultimapShortCircuit(){
108    Multimap<String, Integer> mod = HashMultimap.create();
109    Multimap<String, Integer> unmod = Multimaps.unmodifiableMultimap(mod);
110    assertNotSame(mod, unmod);
111    assertSame(unmod, Multimaps.unmodifiableMultimap(unmod));
112    ImmutableMultimap<String, Integer> immutable = ImmutableMultimap.of("a", 1, "b", 2, "a", 3);
113    assertSame(immutable, Multimaps.unmodifiableMultimap(immutable));
114    assertSame(immutable, Multimaps.unmodifiableMultimap((Multimap<String, Integer>) immutable));
115  }
116
117  @GwtIncompatible("slow (~10s)")
118  public void testUnmodifiableArrayListMultimap() {
119    checkUnmodifiableMultimap(
120        ArrayListMultimap.<String, Integer>create(), true);
121  }
122
123  @GwtIncompatible("SerializableTester")
124  public void testSerializingUnmodifiableArrayListMultimap() {
125    Multimap<String, Integer> unmodifiable =
126        prepareUnmodifiableTests(ArrayListMultimap.<String, Integer>create(), true, null, null);
127    SerializableTester.reserializeAndAssert(unmodifiable);
128  }
129
130  public void testUnmodifiableArrayListMultimapRandomAccess() {
131    ListMultimap<String, Integer> delegate = ArrayListMultimap.create();
132    delegate.put("foo", 1);
133    delegate.put("foo", 3);
134    ListMultimap<String, Integer> multimap
135        = Multimaps.unmodifiableListMultimap(delegate);
136    assertTrue(multimap.get("foo") instanceof RandomAccess);
137    assertTrue(multimap.get("bar") instanceof RandomAccess);
138  }
139
140  public void testUnmodifiableLinkedListMultimapRandomAccess() {
141    ListMultimap<String, Integer> delegate = LinkedListMultimap.create();
142    delegate.put("foo", 1);
143    delegate.put("foo", 3);
144    ListMultimap<String, Integer> multimap
145        = Multimaps.unmodifiableListMultimap(delegate);
146    assertFalse(multimap.get("foo") instanceof RandomAccess);
147    assertFalse(multimap.get("bar") instanceof RandomAccess);
148  }
149
150  @GwtIncompatible("slow (~10s)")
151  public void testUnmodifiableHashMultimap() {
152    checkUnmodifiableMultimap(HashMultimap.<String, Integer>create(), false);
153  }
154
155  @GwtIncompatible("SerializableTester")
156  public void testSerializingUnmodifiableHashMultimap() {
157    Multimap<String, Integer> unmodifiable =
158        prepareUnmodifiableTests(HashMultimap.<String, Integer>create(), false, null, null);
159    SerializableTester.reserializeAndAssert(unmodifiable);
160  }
161
162  @GwtIncompatible("slow (~10s)")
163  public void testUnmodifiableTreeMultimap() {
164    checkUnmodifiableMultimap(
165        TreeMultimap.<String, Integer>create(), false, "null", 42);
166  }
167
168  @GwtIncompatible("SerializableTester")
169  public void testSerializingUnmodifiableTreeMultimap() {
170    Multimap<String, Integer> unmodifiable =
171        prepareUnmodifiableTests(TreeMultimap.<String, Integer>create(), false, "null", 42);
172    SerializableTester.reserializeAndAssert(unmodifiable);
173  }
174
175  @GwtIncompatible("slow (~10s)")
176  public void testUnmodifiableSynchronizedArrayListMultimap() {
177    checkUnmodifiableMultimap(Multimaps.synchronizedListMultimap(
178        ArrayListMultimap.<String, Integer>create()), true);
179  }
180
181  @GwtIncompatible("SerializableTester")
182  public void testSerializingUnmodifiableSynchronizedArrayListMultimap() {
183    Multimap<String, Integer> unmodifiable =
184        prepareUnmodifiableTests(Multimaps.synchronizedListMultimap(
185          ArrayListMultimap.<String, Integer>create()), true, null, null);
186    SerializableTester.reserializeAndAssert(unmodifiable);
187  }
188
189  @GwtIncompatible("slow (~10s)")
190  public void testUnmodifiableSynchronizedHashMultimap() {
191    checkUnmodifiableMultimap(Multimaps.synchronizedSetMultimap(
192        HashMultimap.<String, Integer>create()), false);
193  }
194
195  @GwtIncompatible("SerializableTester")
196  public void testSerializingUnmodifiableSynchronizedHashMultimap() {
197    Multimap<String, Integer> unmodifiable =
198        prepareUnmodifiableTests(Multimaps.synchronizedSetMultimap(
199        HashMultimap.<String, Integer>create()), false, null, null);
200    SerializableTester.reserializeAndAssert(unmodifiable);
201  }
202
203  @GwtIncompatible("slow (~10s)")
204  public void testUnmodifiableSynchronizedTreeMultimap() {
205    TreeMultimap<String, Integer> delegate
206        = TreeMultimap.create(Ordering.<String>natural(), INT_COMPARATOR);
207    SortedSetMultimap<String, Integer> multimap
208        = Multimaps.synchronizedSortedSetMultimap(delegate);
209    checkUnmodifiableMultimap(multimap, false, "null", 42);
210    assertSame(INT_COMPARATOR, multimap.valueComparator());
211  }
212
213  @GwtIncompatible("SerializableTester")
214  public void testSerializingUnmodifiableSynchronizedTreeMultimap() {
215    TreeMultimap<String, Integer> delegate =
216        TreeMultimap.create(Ordering.<String>natural(), INT_COMPARATOR);
217    SortedSetMultimap<String, Integer> multimap =
218        Multimaps.synchronizedSortedSetMultimap(delegate);
219    Multimap<String, Integer> unmodifiable =
220        prepareUnmodifiableTests(multimap, false, "null", 42);
221    SerializableTester.reserializeAndAssert(unmodifiable);
222    assertSame(INT_COMPARATOR, multimap.valueComparator());
223  }
224
225  public void testUnmodifiableMultimapIsView() {
226    Multimap<String, Integer> mod = HashMultimap.create();
227    Multimap<String, Integer> unmod = Multimaps.unmodifiableMultimap(mod);
228    assertEquals(mod, unmod);
229    mod.put("foo", 1);
230    assertTrue(unmod.containsEntry("foo", 1));
231    assertEquals(mod, unmod);
232  }
233
234  @SuppressWarnings("unchecked")
235  public void testUnmodifiableMultimapEntries() {
236    Multimap<String, Integer> mod = HashMultimap.create();
237    Multimap<String, Integer> unmod = Multimaps.unmodifiableMultimap(mod);
238    mod.put("foo", 1);
239    Entry<String, Integer> entry = unmod.entries().iterator().next();
240    try {
241      entry.setValue(2);
242      fail("UnsupportedOperationException expected");
243    } catch (UnsupportedOperationException expected) {}
244    entry = (Entry<String, Integer>) unmod.entries().toArray()[0];
245    try {
246      entry.setValue(2);
247      fail("UnsupportedOperationException expected");
248    } catch (UnsupportedOperationException expected) {}
249    Entry<String, Integer>[] array
250        = (Entry<String, Integer>[]) new Entry<?, ?>[2];
251    assertSame(array, unmod.entries().toArray(array));
252    try {
253      array[0].setValue(2);
254      fail("UnsupportedOperationException expected");
255    } catch (UnsupportedOperationException expected) {}
256    assertFalse(unmod.entries().contains(nefariousMapEntry("pwnd", 2)));
257    assertFalse(unmod.keys().contains("pwnd"));
258  }
259
260  /**
261   * The supplied multimap will be mutated and an unmodifiable instance used
262   * in its stead. The multimap must support null keys and values.
263   */
264  private static void checkUnmodifiableMultimap(
265      Multimap<String, Integer> multimap, boolean permitsDuplicates) {
266    checkUnmodifiableMultimap(multimap, permitsDuplicates, null, null);
267  }
268
269  /**
270   * The supplied multimap will be mutated and an unmodifiable instance used
271   * in its stead. If the multimap does not support null keys or values,
272   * alternatives may be specified for tests involving nulls.
273   */
274  private static void checkUnmodifiableMultimap(
275      Multimap<String, Integer> multimap, boolean permitsDuplicates,
276      @Nullable String nullKey, @Nullable Integer nullValue) {
277    Multimap<String, Integer> unmodifiable =
278        prepareUnmodifiableTests(multimap, permitsDuplicates, nullKey, nullValue);
279
280    UnmodifiableCollectionTests.assertMultimapIsUnmodifiable(
281        unmodifiable, "test", 123);
282
283    assertUnmodifiableIterableInTandem(
284        unmodifiable.keys(), multimap.keys());
285
286    assertUnmodifiableIterableInTandem(
287        unmodifiable.keySet(), multimap.keySet());
288
289    assertUnmodifiableIterableInTandem(
290        unmodifiable.entries(), multimap.entries());
291
292    assertUnmodifiableIterableInTandem(
293        unmodifiable.asMap().entrySet(), multimap.asMap().entrySet());
294
295    assertEquals(multimap.toString(), unmodifiable.toString());
296    assertEquals(multimap.hashCode(), unmodifiable.hashCode());
297    assertEquals(multimap, unmodifiable);
298
299    ASSERT.that(unmodifiable.asMap().get("bar")).hasContentsAnyOrder(5, -1);
300    assertNull(unmodifiable.asMap().get("missing"));
301
302    assertFalse(unmodifiable.entries() instanceof Serializable);
303    assertFalse(unmodifiable.asMap().values() 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 testForMap() {
381    Map<String, Integer> map = Maps.newHashMap();
382    map.put("foo", 1);
383    map.put("bar", 2);
384    Multimap<String, Integer> multimap = HashMultimap.create();
385    multimap.put("foo", 1);
386    multimap.put("bar", 2);
387    Multimap<String, Integer> multimapView = Multimaps.forMap(map);
388    assertTrue(multimap.equals(multimapView));
389    assertTrue(multimapView.equals(multimap));
390    assertTrue(multimapView.equals(multimapView));
391    assertFalse(multimapView.equals(map));
392    Multimap<String, Integer> multimap2 = HashMultimap.create();
393    multimap2.put("foo", 1);
394    assertFalse(multimapView.equals(multimap2));
395    multimap2.put("bar", 1);
396    assertFalse(multimapView.equals(multimap2));
397    ListMultimap<String, Integer> listMultimap
398        = new ImmutableListMultimap.Builder<String, Integer>()
399            .put("foo", 1).put("bar", 2).build();
400    assertFalse("SetMultimap equals ListMultimap",
401        multimapView.equals(listMultimap));
402    assertEquals(multimap.toString(), multimapView.toString());
403    assertEquals(multimap.hashCode(), multimapView.hashCode());
404    assertEquals(multimap.size(), multimapView.size());
405    assertTrue(multimapView.containsKey("foo"));
406    assertTrue(multimapView.containsValue(1));
407    assertTrue(multimapView.containsEntry("bar", 2));
408    assertEquals(Collections.singleton(1), multimapView.get("foo"));
409    assertEquals(Collections.singleton(2), multimapView.get("bar"));
410    try {
411      multimapView.put("baz", 3);
412      fail("UnsupportedOperationException expected");
413    } catch (UnsupportedOperationException expected) {}
414    try {
415      multimapView.putAll("baz", Collections.singleton(3));
416      fail("UnsupportedOperationException expected");
417    } catch (UnsupportedOperationException expected) {}
418    try {
419      multimapView.putAll(multimap);
420      fail("UnsupportedOperationException expected");
421    } catch (UnsupportedOperationException expected) {}
422    try {
423      multimapView.replaceValues("foo", Collections.<Integer>emptySet());
424      fail("UnsupportedOperationException expected");
425    } catch (UnsupportedOperationException expected) {}
426    multimapView.remove("bar", 2);
427    assertFalse(multimapView.containsKey("bar"));
428    assertFalse(map.containsKey("bar"));
429    assertEquals(map.keySet(), multimapView.keySet());
430    assertEquals(map.keySet(), multimapView.keys().elementSet());
431    ASSERT.that(multimapView.keys()).hasContentsAnyOrder("foo");
432    ASSERT.that(multimapView.values()).hasContentsAnyOrder(1);
433    ASSERT.that(multimapView.entries()).hasContentsAnyOrder(
434        Maps.immutableEntry("foo", 1));
435    ASSERT.that(multimapView.asMap().entrySet()).hasContentsAnyOrder(
436        Maps.immutableEntry(
437            "foo", (Collection<Integer>) Collections.singleton(1)));
438    multimapView.clear();
439    assertFalse(multimapView.containsKey("foo"));
440    assertFalse(map.containsKey("foo"));
441    assertTrue(map.isEmpty());
442    assertTrue(multimapView.isEmpty());
443    multimap.clear();
444    assertEquals(multimap.toString(), multimapView.toString());
445    assertEquals(multimap.hashCode(), multimapView.hashCode());
446    assertEquals(multimap.size(), multimapView.size());
447    assertEquals(multimapView, ArrayListMultimap.create());
448  }
449
450  @GwtIncompatible("SerializableTester")
451  public void testForMapSerialization() {
452    Map<String, Integer> map = Maps.newHashMap();
453    map.put("foo", 1);
454    map.put("bar", 2);
455    Multimap<String, Integer> multimapView = Multimaps.forMap(map);
456    SerializableTester.reserializeAndAssert(multimapView);
457  }
458
459  public void testForMapRemoveAll() {
460    Map<String, Integer> map = Maps.newHashMap();
461    map.put("foo", 1);
462    map.put("bar", 2);
463    map.put("cow", 3);
464    Multimap<String, Integer> multimap = Multimaps.forMap(map);
465    assertEquals(3, multimap.size());
466    assertEquals(Collections.emptySet(), multimap.removeAll("dog"));
467    assertEquals(3, multimap.size());
468    assertTrue(multimap.containsKey("bar"));
469    assertEquals(Collections.singleton(2), multimap.removeAll("bar"));
470    assertEquals(2, multimap.size());
471    assertFalse(multimap.containsKey("bar"));
472  }
473
474  public void testForMapAsMap() {
475    Map<String, Integer> map = Maps.newHashMap();
476    map.put("foo", 1);
477    map.put("bar", 2);
478    Map<String, Collection<Integer>> asMap = Multimaps.forMap(map).asMap();
479    assertEquals(Collections.singleton(1), asMap.get("foo"));
480    assertNull(asMap.get("cow"));
481    assertTrue(asMap.containsKey("foo"));
482    assertFalse(asMap.containsKey("cow"));
483
484    Set<Entry<String, Collection<Integer>>> entries = asMap.entrySet();
485    assertFalse(entries.contains(4.5));
486    assertFalse(entries.remove(4.5));
487    assertFalse(entries.contains(Maps.immutableEntry("foo",
488        Collections.singletonList(1))));
489    assertFalse(entries.remove(Maps.immutableEntry("foo",
490        Collections.singletonList(1))));
491    assertFalse(entries.contains(Maps.immutableEntry("foo",
492        Sets.newLinkedHashSet(asList(1, 2)))));
493    assertFalse(entries.remove(Maps.immutableEntry("foo",
494        Sets.newLinkedHashSet(asList(1, 2)))));
495    assertFalse(entries.contains(Maps.immutableEntry("foo",
496        Collections.singleton(2))));
497    assertFalse(entries.remove(Maps.immutableEntry("foo",
498        Collections.singleton(2))));
499    assertTrue(map.containsKey("foo"));
500    assertTrue(entries.contains(Maps.immutableEntry("foo",
501        Collections.singleton(1))));
502    assertTrue(entries.remove(Maps.immutableEntry("foo",
503        Collections.singleton(1))));
504    assertFalse(map.containsKey("foo"));
505  }
506
507  public void testForMapGetIteration() {
508    IteratorTester<Integer> tester =
509        new IteratorTester<Integer>(4, MODIFIABLE, newHashSet(1),
510            IteratorTester.KnownOrder.KNOWN_ORDER) {
511          private Multimap<String, Integer> multimap;
512
513          @Override protected Iterator<Integer> newTargetIterator() {
514            Map<String, Integer> map = Maps.newHashMap();
515            map.put("foo", 1);
516            map.put("bar", 2);
517            multimap = Multimaps.forMap(map);
518            return multimap.get("foo").iterator();
519          }
520
521          @Override protected void verify(List<Integer> elements) {
522            assertEquals(newHashSet(elements), multimap.get("foo"));
523          }
524        };
525
526    tester.ignoreSunJavaBug6529795();
527    tester.test();
528  }
529
530  private enum Color {BLUE, RED, YELLOW, GREEN}
531
532  private static abstract class CountingSupplier<E>
533      implements Supplier<E>, Serializable {
534    int count;
535
536    abstract E getImpl();
537
538    @Override
539    public E get() {
540      count++;
541      return getImpl();
542    }
543  }
544
545  private static class QueueSupplier extends CountingSupplier<Queue<Integer>> {
546    @Override public Queue<Integer> getImpl() {
547      return new LinkedList<Integer>();
548    }
549    private static final long serialVersionUID = 0;
550  }
551
552  public void testNewMultimap() {
553    // The ubiquitous EnumArrayBlockingQueueMultimap
554    CountingSupplier<Queue<Integer>> factory = new QueueSupplier();
555
556    Map<Color, Collection<Integer>> map = Maps.newEnumMap(Color.class);
557    Multimap<Color, Integer> multimap = Multimaps.newMultimap(map, factory);
558    assertEquals(0, factory.count);
559    multimap.putAll(Color.BLUE, asList(3, 1, 4));
560    assertEquals(1, factory.count);
561    multimap.putAll(Color.RED, asList(2, 7, 1, 8));
562    assertEquals(2, factory.count);
563    assertEquals("[3, 1, 4]", multimap.get(Color.BLUE).toString());
564
565    Multimap<Color, Integer> ummodifiable =
566        Multimaps.unmodifiableMultimap(multimap);
567    assertEquals("[3, 1, 4]", ummodifiable.get(Color.BLUE).toString());
568
569    Collection<Integer> collection = multimap.get(Color.BLUE);
570    assertEquals(collection, collection);
571
572    assertFalse(multimap.keySet() instanceof SortedSet);
573    assertFalse(multimap.asMap() instanceof SortedMap);
574  }
575
576  @GwtIncompatible("SerializableTester")
577  public void testNewMultimapSerialization() {
578    CountingSupplier<Queue<Integer>> factory = new QueueSupplier();
579    Map<Color, Collection<Integer>> map = Maps.newEnumMap(Color.class);
580    Multimap<Color, Integer> multimap = Multimaps.newMultimap(map, factory);
581    multimap.putAll(Color.BLUE, asList(3, 1, 4));
582    multimap.putAll(Color.RED, asList(2, 7, 1, 8));
583    SerializableTester.reserializeAndAssert(multimap);
584  }
585
586  private static class ListSupplier extends
587      CountingSupplier<LinkedList<Integer>> {
588    @Override public LinkedList<Integer> getImpl() {
589      return new LinkedList<Integer>();
590    }
591    private static final long serialVersionUID = 0;
592  }
593
594  public void testNewListMultimap() {
595    CountingSupplier<LinkedList<Integer>> factory = new ListSupplier();
596    Map<Color, Collection<Integer>> map = Maps.newTreeMap();
597    ListMultimap<Color, Integer> multimap =
598        Multimaps.newListMultimap(map, factory);
599    assertEquals(0, factory.count);
600    multimap.putAll(Color.BLUE, asList(3, 1, 4, 1));
601    assertEquals(1, factory.count);
602    multimap.putAll(Color.RED, asList(2, 7, 1, 8));
603    assertEquals(2, factory.count);
604    assertEquals("{BLUE=[3, 1, 4, 1], RED=[2, 7, 1, 8]}", multimap.toString());
605    assertFalse(multimap.get(Color.BLUE) instanceof RandomAccess);
606
607    assertTrue(multimap.keySet() instanceof SortedSet);
608    assertTrue(multimap.asMap() instanceof SortedMap);
609  }
610
611  @GwtIncompatible("SerializableTester")
612  public void testNewListMultimapSerialization() {
613    CountingSupplier<LinkedList<Integer>> factory = new ListSupplier();
614    Map<Color, Collection<Integer>> map = Maps.newTreeMap();
615    ListMultimap<Color, Integer> multimap = Multimaps.newListMultimap(map, factory);
616    multimap.putAll(Color.BLUE, asList(3, 1, 4, 1));
617    multimap.putAll(Color.RED, asList(2, 7, 1, 8));
618    SerializableTester.reserializeAndAssert(multimap);
619  }
620
621  private static class SetSupplier extends CountingSupplier<HashSet<Integer>> {
622    @Override public HashSet<Integer> getImpl() {
623      return new HashSet<Integer>(4);
624    }
625    private static final long serialVersionUID = 0;
626  }
627
628  public void testNewSetMultimap() {
629    CountingSupplier<HashSet<Integer>> factory = new SetSupplier();
630    Map<Color, Collection<Integer>> map = Maps.newHashMap();
631    SetMultimap<Color, Integer> multimap =
632        Multimaps.newSetMultimap(map, factory);
633    assertEquals(0, factory.count);
634    multimap.putAll(Color.BLUE, asList(3, 1, 4));
635    assertEquals(1, factory.count);
636    multimap.putAll(Color.RED, asList(2, 7, 1, 8));
637    assertEquals(2, factory.count);
638    assertEquals(Sets.newHashSet(4, 3, 1), multimap.get(Color.BLUE));
639  }
640
641  @GwtIncompatible("SerializableTester")
642  public void testNewSetMultimapSerialization() {
643    CountingSupplier<HashSet<Integer>> factory = new SetSupplier();
644    Map<Color, Collection<Integer>> map = Maps.newHashMap();
645    SetMultimap<Color, Integer> multimap = Multimaps.newSetMultimap(map, factory);
646    multimap.putAll(Color.BLUE, asList(3, 1, 4));
647    multimap.putAll(Color.RED, asList(2, 7, 1, 8));
648    SerializableTester.reserializeAndAssert(multimap);
649  }
650
651  private static class SortedSetSupplier extends
652      CountingSupplier<TreeSet<Integer>> {
653    @Override public TreeSet<Integer> getImpl() {
654      return Sets.newTreeSet(INT_COMPARATOR);
655    }
656    private static final long serialVersionUID = 0;
657  }
658
659  public void testNewSortedSetMultimap() {
660    CountingSupplier<TreeSet<Integer>> factory = new SortedSetSupplier();
661    Map<Color, Collection<Integer>> map = Maps.newEnumMap(Color.class);
662    SortedSetMultimap<Color, Integer> multimap =
663        Multimaps.newSortedSetMultimap(map, factory);
664    // newSortedSetMultimap calls the factory once to determine the comparator.
665    assertEquals(1, factory.count);
666    multimap.putAll(Color.BLUE, asList(3, 1, 4));
667    assertEquals(2, factory.count);
668    multimap.putAll(Color.RED, asList(2, 7, 1, 8));
669    assertEquals(3, factory.count);
670    assertEquals("[4, 3, 1]", multimap.get(Color.BLUE).toString());
671    assertEquals(INT_COMPARATOR, multimap.valueComparator());
672  }
673
674  @GwtIncompatible("SerializableTester")
675  public void testNewSortedSetMultimapSerialization() {
676    CountingSupplier<TreeSet<Integer>> factory = new SortedSetSupplier();
677    Map<Color, Collection<Integer>> map = Maps.newEnumMap(Color.class);
678    SortedSetMultimap<Color, Integer> multimap = Multimaps.newSortedSetMultimap(map, factory);
679    multimap.putAll(Color.BLUE, asList(3, 1, 4));
680    multimap.putAll(Color.RED, asList(2, 7, 1, 8));
681    SerializableTester.reserializeAndAssert(multimap);
682    assertEquals(INT_COMPARATOR, multimap.valueComparator());
683  }
684
685  public void testIndex() {
686    final Multimap<String, Object> stringToObject =
687        new ImmutableMultimap.Builder<String, Object>()
688            .put("1", 1)
689            .put("1", 1L)
690            .put("1", "1")
691            .put("2", 2)
692            .put("2", 2L)
693            .build();
694
695    ImmutableMultimap<String, Object> outputMap =
696        Multimaps.index(stringToObject.values(),
697            Functions.toStringFunction());
698    assertEquals(stringToObject, outputMap);
699  }
700
701  public void testIndexIterator() {
702    final Multimap<String, Object> stringToObject =
703        new ImmutableMultimap.Builder<String, Object>()
704            .put("1", 1)
705            .put("1", 1L)
706            .put("1", "1")
707            .put("2", 2)
708            .put("2", 2L)
709            .build();
710
711    ImmutableMultimap<String, Object> outputMap =
712        Multimaps.index(stringToObject.values().iterator(),
713            Functions.toStringFunction());
714    assertEquals(stringToObject, outputMap);
715  }
716
717  // NOTE: evil, never do this
718  private abstract static class IterableIterator<T>
719      extends ForwardingIterator<T> implements Iterable<T> {
720    @Override
721    public Iterator<T> iterator() {
722      return this;
723    }
724  }
725
726  @SuppressWarnings("deprecation") // that is the purpose of this test
727  public void testIndexIterableIterator() {
728    final Multimap<String, Object> stringToObject =
729        new ImmutableMultimap.Builder<String, Object>()
730            .put("1", 1)
731            .put("1", 1L)
732            .put("1", "1")
733            .put("2", 2)
734            .put("2", 2L)
735            .build();
736
737    IterableIterator<Object> iterIter = new IterableIterator<Object>() {
738      private final Iterator<Object> iterator = stringToObject.values().iterator();
739
740      public Iterator<Object> delegate() {
741        return iterator;
742      }
743    };
744
745    ImmutableMultimap<String, Object> outputMap =
746        Multimaps.index(iterIter, Functions.toStringFunction());
747    assertEquals(stringToObject, outputMap);
748  }
749
750  public void testIndex_ordering() {
751    final Multimap<Integer, String> expectedIndex =
752        new ImmutableListMultimap.Builder<Integer, String>()
753            .put(4, "Inky")
754            .put(6, "Blinky")
755            .put(5, "Pinky")
756            .put(5, "Pinky")
757            .put(5, "Clyde")
758            .build();
759
760    final List<String> badGuys =
761        Arrays.asList("Inky", "Blinky", "Pinky", "Pinky", "Clyde");
762    final Function<String, Integer> stringLengthFunction =
763        new Function<String, Integer>() {
764          @Override
765          public Integer apply(String input) {
766            return input.length();
767          }
768        };
769
770    Multimap<Integer, String> index =
771        Multimaps.index(badGuys, stringLengthFunction);
772
773    assertEquals(expectedIndex, index);
774  }
775
776  public void testIndex_nullValue() {
777    List<Integer> values = Arrays.asList(1, null);
778    try {
779      Multimaps.index(values, Functions.identity());
780      fail();
781    } catch (NullPointerException e) {}
782  }
783
784  public void testIndex_nullKey() {
785    List<Integer> values = Arrays.asList(1, 2);
786    try {
787      Multimaps.index(values, Functions.constant(null));
788      fail();
789    } catch (NullPointerException e) {}
790  }
791
792  @GwtIncompatible(value = "untested")
793  public void testTransformValues() {
794    SetMultimap<String, Integer> multimap =
795        ImmutableSetMultimap.of("a", 2, "b", -3, "b", 3, "a", 4, "c", 6);
796    Function<Integer, Integer> square = new Function<Integer, Integer>() {
797      @Override
798      public Integer apply(Integer in) {
799        return in * in;
800      }
801    };
802    Multimap<String, Integer> transformed =
803        Multimaps.transformValues(multimap, square);
804    ASSERT.that(transformed.entries()).hasContentsInOrder(immutableEntry("a", 4),
805        immutableEntry("a", 16), immutableEntry("b", 9), immutableEntry("b", 9),
806        immutableEntry("c", 36));
807  }
808
809  @GwtIncompatible(value = "untested")
810  public void testTransformValuesIsView() {
811    Multimap<String, String> multimap = LinkedListMultimap.create();
812    multimap.put("a", "a");
813    Multimap<String, Integer> transformed =
814        Multimaps.transformValues(multimap, new Function<String, Integer>() {
815
816          @Override public Integer apply(String str) {
817            return str.length();
818          }
819        });
820    Entry<String, String> entry = multimap.entries().iterator().next();
821    entry.setValue("bbb");
822    ASSERT.that(transformed.entries()).hasContentsInOrder(immutableEntry("a", 3));
823  }
824
825  @GwtIncompatible(value = "untested")
826  public void testTransformListValues() {
827    ListMultimap<String, Integer> multimap =
828        ImmutableListMultimap.of("a", 2, "b", -3, "b", 3, "a", 4, "c", 6);
829    Function<Integer, Integer> square = new Function<Integer, Integer>() {
830      @Override
831      public Integer apply(Integer in) {
832        return in * in;
833      }
834    };
835    ListMultimap<String, Integer> transformed =
836        Multimaps.transformValues(multimap, square);
837    ASSERT.that(transformed.entries()).hasContentsInOrder(immutableEntry("a", 4),
838        immutableEntry("a", 16), immutableEntry("b", 9), immutableEntry("b", 9),
839        immutableEntry("c", 36));
840  }
841
842  @GwtIncompatible(value = "untested")
843  public void testTransformEntries() {
844    SetMultimap<String, Integer> multimap =
845        ImmutableSetMultimap.of("a", 1, "a", 4, "b", -6);
846    EntryTransformer<String, Integer, String> transformer =
847        new EntryTransformer<String, Integer, String>() {
848          @Override
849          public String transformEntry(String key, Integer value) {
850            return (value >= 0) ? key : "no" + key;
851          }
852        };
853    Multimap<String, String> transformed =
854        Multimaps.transformEntries(multimap, transformer);
855    ASSERT.that(transformed.entries()).hasContentsInOrder(immutableEntry("a", "a"),
856        immutableEntry("a", "a"), immutableEntry("b", "nob"));
857  }
858
859  @GwtIncompatible(value = "untested")
860  public void testTransformListEntries() {
861    ListMultimap<String, Integer> multimap =
862        ImmutableListMultimap.of("a", 1, "a", 4, "b", 6, "a", 4);
863    EntryTransformer<String, Integer, String> transformer =
864        new EntryTransformer<String, Integer, String>() {
865          @Override
866          public String transformEntry(String key, Integer value) {
867            return key + value;
868          }
869        };
870    ListMultimap<String, String> transformed =
871        Multimaps.transformEntries(multimap, transformer);
872    assertEquals(
873        ImmutableListMultimap.of("a", "a1", "a", "a4", "a", "a4", "b", "b6"),
874        transformed);
875    assertEquals("{a=[a1, a4, a4], b=[b6]}", transformed.toString());
876  }
877
878  @GwtIncompatible("NullPointerTester")
879  public void testNullPointers() throws Exception {
880    NullPointerTester tester = new NullPointerTester();
881    tester.setDefault(Multimap.class, ImmutableMultimap.of());
882    tester.setDefault(ListMultimap.class, ImmutableListMultimap.of());
883    tester.setDefault(EntryTransformer.class, ALWAYS_NULL);
884    tester.ignore(Multimaps.class.getDeclaredMethod("index", Object.class, Function.class));
885    tester.testAllPublicStaticMethods(Multimaps.class);
886  }
887}
888