1/*
2 * Copyright (C) 2008 The Guava Authors
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.google.common.collect;
18
19import static com.google.common.truth.Truth.assertThat;
20
21import com.google.common.annotations.GwtCompatible;
22import com.google.common.annotations.GwtIncompatible;
23import com.google.common.collect.ImmutableListMultimap.Builder;
24import com.google.common.collect.testing.features.CollectionFeature;
25import com.google.common.collect.testing.features.CollectionSize;
26import com.google.common.collect.testing.features.MapFeature;
27import com.google.common.collect.testing.google.ListMultimapTestSuiteBuilder;
28import com.google.common.collect.testing.google.TestStringListMultimapGenerator;
29import com.google.common.collect.testing.google.UnmodifiableCollectionTests;
30import com.google.common.testing.EqualsTester;
31import com.google.common.testing.SerializableTester;
32
33import junit.framework.Test;
34import junit.framework.TestCase;
35import junit.framework.TestSuite;
36
37import java.util.Arrays;
38import java.util.Collection;
39import java.util.Collections;
40import java.util.Map.Entry;
41
42/**
43 * Tests for {@link ImmutableListMultimap}.
44 *
45 * @author Jared Levy
46 */
47@GwtCompatible(emulated = true)
48public class ImmutableListMultimapTest extends TestCase {
49  public static class ImmutableListMultimapGenerator extends TestStringListMultimapGenerator {
50    @Override
51    protected ListMultimap<String, String> create(Entry<String, String>[] entries) {
52      ImmutableListMultimap.Builder<String, String> builder = ImmutableListMultimap.builder();
53      for (Entry<String, String> entry : entries) {
54        builder.put(entry.getKey(), entry.getValue());
55      }
56      return builder.build();
57    }
58  }
59
60  @GwtIncompatible("suite")
61  public static Test suite() {
62    TestSuite suite = new TestSuite();
63    suite.addTest(ListMultimapTestSuiteBuilder.using(new ImmutableListMultimapGenerator())
64      .named("ImmutableListMultimap")
65      .withFeatures(
66          MapFeature.ALLOWS_ANY_NULL_QUERIES,
67          CollectionFeature.SERIALIZABLE,
68          CollectionFeature.KNOWN_ORDER,
69          CollectionSize.ANY)
70      .createTestSuite());
71    suite.addTestSuite(ImmutableListMultimapTest.class);
72    return suite;
73  }
74
75  public void testBuilder_withImmutableEntry() {
76    ImmutableListMultimap<String, Integer> multimap = new Builder<String, Integer>()
77        .put(Maps.immutableEntry("one", 1))
78        .build();
79    assertEquals(Arrays.asList(1), multimap.get("one"));
80  }
81
82  public void testBuilder_withImmutableEntryAndNullContents() {
83    Builder<String, Integer> builder = new Builder<String, Integer>();
84    try {
85      builder.put(Maps.immutableEntry("one", (Integer) null));
86      fail();
87    } catch (NullPointerException expected) {
88    }
89    try {
90      builder.put(Maps.immutableEntry((String) null, 1));
91      fail();
92    } catch (NullPointerException expected) {
93    }
94  }
95
96  private static class StringHolder {
97    String string;
98  }
99
100  public void testBuilder_withMutableEntry() {
101    ImmutableListMultimap.Builder<String, Integer> builder =
102        new Builder<String, Integer>();
103    final StringHolder holder = new StringHolder();
104    holder.string = "one";
105    Entry<String, Integer> entry = new AbstractMapEntry<String, Integer>() {
106      @Override public String getKey() {
107        return holder.string;
108      }
109      @Override public Integer getValue() {
110        return 1;
111      }
112    };
113
114    builder.put(entry);
115    holder.string = "two";
116    assertEquals(Arrays.asList(1), builder.build().get("one"));
117  }
118
119  public void testBuilderPutAllIterable() {
120    ImmutableListMultimap.Builder<String, Integer> builder
121        = ImmutableListMultimap.builder();
122    builder.putAll("foo", Arrays.asList(1, 2, 3));
123    builder.putAll("bar", Arrays.asList(4, 5));
124    builder.putAll("foo", Arrays.asList(6, 7));
125    Multimap<String, Integer> multimap = builder.build();
126    assertEquals(Arrays.asList(1, 2, 3, 6, 7), multimap.get("foo"));
127    assertEquals(Arrays.asList(4, 5), multimap.get("bar"));
128    assertEquals(7, multimap.size());
129  }
130
131  public void testBuilderPutAllVarargs() {
132    ImmutableListMultimap.Builder<String, Integer> builder
133        = ImmutableListMultimap.builder();
134    builder.putAll("foo", 1, 2, 3);
135    builder.putAll("bar", 4, 5);
136    builder.putAll("foo", 6, 7);
137    Multimap<String, Integer> multimap = builder.build();
138    assertEquals(Arrays.asList(1, 2, 3, 6, 7), multimap.get("foo"));
139    assertEquals(Arrays.asList(4, 5), multimap.get("bar"));
140    assertEquals(7, multimap.size());
141  }
142
143  public void testBuilderPutAllMultimap() {
144    Multimap<String, Integer> toPut = LinkedListMultimap.create();
145    toPut.put("foo", 1);
146    toPut.put("bar", 4);
147    toPut.put("foo", 2);
148    toPut.put("foo", 3);
149    Multimap<String, Integer> moreToPut = LinkedListMultimap.create();
150    moreToPut.put("foo", 6);
151    moreToPut.put("bar", 5);
152    moreToPut.put("foo", 7);
153    ImmutableListMultimap.Builder<String, Integer> builder
154        = ImmutableListMultimap.builder();
155    builder.putAll(toPut);
156    builder.putAll(moreToPut);
157    Multimap<String, Integer> multimap = builder.build();
158    assertEquals(Arrays.asList(1, 2, 3, 6, 7), multimap.get("foo"));
159    assertEquals(Arrays.asList(4, 5), multimap.get("bar"));
160    assertEquals(7, multimap.size());
161  }
162
163  public void testBuilderPutAllWithDuplicates() {
164    ImmutableListMultimap.Builder<String, Integer> builder
165        = ImmutableListMultimap.builder();
166    builder.putAll("foo", 1, 2, 3);
167    builder.putAll("bar", 4, 5);
168    builder.putAll("foo", 1, 6, 7);
169    ImmutableListMultimap<String, Integer> multimap = builder.build();
170    assertEquals(Arrays.asList(1, 2, 3, 1, 6, 7), multimap.get("foo"));
171    assertEquals(Arrays.asList(4, 5), multimap.get("bar"));
172    assertEquals(8, multimap.size());
173  }
174
175  public void testBuilderPutWithDuplicates() {
176    ImmutableListMultimap.Builder<String, Integer> builder
177        = ImmutableListMultimap.builder();
178    builder.putAll("foo", 1, 2, 3);
179    builder.putAll("bar", 4, 5);
180    builder.put("foo", 1);
181    ImmutableListMultimap<String, Integer> multimap = builder.build();
182    assertEquals(Arrays.asList(1, 2, 3, 1), multimap.get("foo"));
183    assertEquals(Arrays.asList(4, 5), multimap.get("bar"));
184    assertEquals(6, multimap.size());
185  }
186
187  public void testBuilderPutAllMultimapWithDuplicates() {
188    Multimap<String, Integer> toPut = LinkedListMultimap.create();
189    toPut.put("foo", 1);
190    toPut.put("bar", 4);
191    toPut.put("foo", 2);
192    toPut.put("foo", 1);
193    toPut.put("bar", 5);
194    Multimap<String, Integer> moreToPut = LinkedListMultimap.create();
195    moreToPut.put("foo", 6);
196    moreToPut.put("bar", 4);
197    moreToPut.put("foo", 7);
198    moreToPut.put("foo", 2);
199    ImmutableListMultimap.Builder<String, Integer> builder
200        = ImmutableListMultimap.builder();
201    builder.putAll(toPut);
202    builder.putAll(moreToPut);
203    Multimap<String, Integer> multimap = builder.build();
204    assertEquals(Arrays.asList(1, 2, 1, 6, 7, 2), multimap.get("foo"));
205    assertEquals(Arrays.asList(4, 5, 4), multimap.get("bar"));
206    assertEquals(9, multimap.size());
207  }
208
209  public void testBuilderPutNullKey() {
210    Multimap<String, Integer> toPut = LinkedListMultimap.create();
211    toPut.put("foo", null);
212    ImmutableListMultimap.Builder<String, Integer> builder
213        = ImmutableListMultimap.builder();
214    try {
215      builder.put(null, 1);
216      fail();
217    } catch (NullPointerException expected) {}
218    try {
219      builder.putAll(null, Arrays.asList(1, 2, 3));
220      fail();
221    } catch (NullPointerException expected) {}
222    try {
223      builder.putAll(null, 1, 2, 3);
224      fail();
225    } catch (NullPointerException expected) {}
226    try {
227      builder.putAll(toPut);
228      fail();
229    } catch (NullPointerException expected) {}
230  }
231
232  public void testBuilderPutNullValue() {
233    Multimap<String, Integer> toPut = LinkedListMultimap.create();
234    toPut.put(null, 1);
235    ImmutableListMultimap.Builder<String, Integer> builder
236        = ImmutableListMultimap.builder();
237    try {
238      builder.put("foo", null);
239      fail();
240    } catch (NullPointerException expected) {}
241    try {
242      builder.putAll("foo", Arrays.asList(1, null, 3));
243      fail();
244    } catch (NullPointerException expected) {}
245    try {
246      builder.putAll("foo", 1, null, 3);
247      fail();
248    } catch (NullPointerException expected) {}
249    try {
250      builder.putAll(toPut);
251      fail();
252    } catch (NullPointerException expected) {}
253  }
254
255  public void testBuilderOrderKeysBy() {
256    ImmutableListMultimap.Builder<String, Integer> builder
257        = ImmutableListMultimap.builder();
258    builder.put("b", 3);
259    builder.put("d", 2);
260    builder.put("a", 5);
261    builder.orderKeysBy(Collections.reverseOrder());
262    builder.put("c", 4);
263    builder.put("a", 2);
264    builder.put("b", 6);
265    ImmutableListMultimap<String, Integer> multimap = builder.build();
266    assertThat(multimap.keySet()).has().exactly("d", "c", "b", "a").inOrder();
267    assertThat(multimap.values()).has().exactly(2, 4, 3, 6, 5, 2).inOrder();
268    assertThat(multimap.get("a")).has().exactly(5, 2).inOrder();
269    assertThat(multimap.get("b")).has().exactly(3, 6).inOrder();
270  }
271
272  public void testBuilderOrderKeysByDuplicates() {
273    ImmutableListMultimap.Builder<String, Integer> builder
274        = ImmutableListMultimap.builder();
275    builder.put("bb", 3);
276    builder.put("d", 2);
277    builder.put("a", 5);
278    builder.orderKeysBy(new Ordering<String>() {
279      @Override
280      public int compare(String left, String right) {
281        return left.length() - right.length();
282      }
283    });
284    builder.put("cc", 4);
285    builder.put("a", 2);
286    builder.put("bb", 6);
287    ImmutableListMultimap<String, Integer> multimap = builder.build();
288    assertThat(multimap.keySet()).has().exactly("d", "a", "bb", "cc").inOrder();
289    assertThat(multimap.values()).has().exactly(2, 5, 2, 3, 6, 4).inOrder();
290    assertThat(multimap.get("a")).has().exactly(5, 2).inOrder();
291    assertThat(multimap.get("bb")).has().exactly(3, 6).inOrder();
292  }
293
294  public void testBuilderOrderValuesBy() {
295    ImmutableListMultimap.Builder<String, Integer> builder
296        = ImmutableListMultimap.builder();
297    builder.put("b", 3);
298    builder.put("d", 2);
299    builder.put("a", 5);
300    builder.orderValuesBy(Collections.reverseOrder());
301    builder.put("c", 4);
302    builder.put("a", 2);
303    builder.put("b", 6);
304    ImmutableListMultimap<String, Integer> multimap = builder.build();
305    assertThat(multimap.keySet()).has().exactly("b", "d", "a", "c").inOrder();
306    assertThat(multimap.values()).has().exactly(6, 3, 2, 5, 2, 4).inOrder();
307    assertThat(multimap.get("a")).has().exactly(5, 2).inOrder();
308    assertThat(multimap.get("b")).has().exactly(6, 3).inOrder();
309  }
310
311  public void testBuilderOrderKeysAndValuesBy() {
312    ImmutableListMultimap.Builder<String, Integer> builder
313        = ImmutableListMultimap.builder();
314    builder.put("b", 3);
315    builder.put("d", 2);
316    builder.put("a", 5);
317    builder.orderKeysBy(Collections.reverseOrder());
318    builder.orderValuesBy(Collections.reverseOrder());
319    builder.put("c", 4);
320    builder.put("a", 2);
321    builder.put("b", 6);
322    ImmutableListMultimap<String, Integer> multimap = builder.build();
323    assertThat(multimap.keySet()).has().exactly("d", "c", "b", "a").inOrder();
324    assertThat(multimap.values()).has().exactly(2, 4, 6, 3, 5, 2).inOrder();
325    assertThat(multimap.get("a")).has().exactly(5, 2).inOrder();
326    assertThat(multimap.get("b")).has().exactly(6, 3).inOrder();
327  }
328
329  public void testCopyOf() {
330    ArrayListMultimap<String, Integer> input = ArrayListMultimap.create();
331    input.put("foo", 1);
332    input.put("bar", 2);
333    input.put("foo", 3);
334    Multimap<String, Integer> multimap = ImmutableListMultimap.copyOf(input);
335    assertEquals(multimap, input);
336    assertEquals(input, multimap);
337  }
338
339  public void testCopyOfWithDuplicates() {
340    ArrayListMultimap<String, Integer> input = ArrayListMultimap.create();
341    input.put("foo", 1);
342    input.put("bar", 2);
343    input.put("foo", 3);
344    input.put("foo", 1);
345    Multimap<String, Integer> multimap = ImmutableListMultimap.copyOf(input);
346    assertEquals(multimap, input);
347    assertEquals(input, multimap);
348  }
349
350  public void testCopyOfEmpty() {
351    ArrayListMultimap<String, Integer> input = ArrayListMultimap.create();
352    Multimap<String, Integer> multimap = ImmutableListMultimap.copyOf(input);
353    assertEquals(multimap, input);
354    assertEquals(input, multimap);
355  }
356
357  public void testCopyOfImmutableListMultimap() {
358    Multimap<String, Integer> multimap = createMultimap();
359    assertSame(multimap, ImmutableListMultimap.copyOf(multimap));
360  }
361
362  public void testCopyOfNullKey() {
363    ArrayListMultimap<String, Integer> input = ArrayListMultimap.create();
364    input.put(null, 1);
365    try {
366      ImmutableListMultimap.copyOf(input);
367      fail();
368    } catch (NullPointerException expected) {}
369  }
370
371  public void testCopyOfNullValue() {
372    ArrayListMultimap<String, Integer> input = ArrayListMultimap.create();
373    input.putAll("foo", Arrays.asList(1, null, 3));
374    try {
375      ImmutableListMultimap.copyOf(input);
376      fail();
377    } catch (NullPointerException expected) {}
378  }
379
380  public void testEmptyMultimapReads() {
381    Multimap<String, Integer> multimap = ImmutableListMultimap.of();
382    assertFalse(multimap.containsKey("foo"));
383    assertFalse(multimap.containsValue(1));
384    assertFalse(multimap.containsEntry("foo", 1));
385    assertTrue(multimap.entries().isEmpty());
386    assertTrue(multimap.equals(ArrayListMultimap.create()));
387    assertEquals(Collections.emptyList(), multimap.get("foo"));
388    assertEquals(0, multimap.hashCode());
389    assertTrue(multimap.isEmpty());
390    assertEquals(HashMultiset.create(), multimap.keys());
391    assertEquals(Collections.emptySet(), multimap.keySet());
392    assertEquals(0, multimap.size());
393    assertTrue(multimap.values().isEmpty());
394    assertEquals("{}", multimap.toString());
395  }
396
397  public void testEmptyMultimapWrites() {
398    Multimap<String, Integer> multimap = ImmutableListMultimap.of();
399    UnmodifiableCollectionTests.assertMultimapIsUnmodifiable(
400        multimap, "foo", 1);
401  }
402
403  private Multimap<String, Integer> createMultimap() {
404    return ImmutableListMultimap.<String, Integer>builder()
405        .put("foo", 1).put("bar", 2).put("foo", 3).build();
406  }
407
408  public void testMultimapReads() {
409    Multimap<String, Integer> multimap = createMultimap();
410    assertTrue(multimap.containsKey("foo"));
411    assertFalse(multimap.containsKey("cat"));
412    assertTrue(multimap.containsValue(1));
413    assertFalse(multimap.containsValue(5));
414    assertTrue(multimap.containsEntry("foo", 1));
415    assertFalse(multimap.containsEntry("cat", 1));
416    assertFalse(multimap.containsEntry("foo", 5));
417    assertFalse(multimap.entries().isEmpty());
418    assertEquals(3, multimap.size());
419    assertFalse(multimap.isEmpty());
420    assertEquals("{foo=[1, 3], bar=[2]}", multimap.toString());
421  }
422
423  public void testMultimapWrites() {
424    Multimap<String, Integer> multimap = createMultimap();
425    UnmodifiableCollectionTests.assertMultimapIsUnmodifiable(
426        multimap, "bar", 2);
427  }
428
429  public void testMultimapEquals() {
430    Multimap<String, Integer> multimap = createMultimap();
431    Multimap<String, Integer> arrayListMultimap
432        = ArrayListMultimap.create();
433    arrayListMultimap.putAll("foo", Arrays.asList(1, 3));
434    arrayListMultimap.put("bar", 2);
435
436    new EqualsTester()
437        .addEqualityGroup(multimap, createMultimap(), arrayListMultimap,
438            ImmutableListMultimap.<String, Integer>builder()
439                .put("bar", 2).put("foo", 1).put("foo", 3).build())
440        .addEqualityGroup(ImmutableListMultimap.<String, Integer>builder()
441            .put("bar", 2).put("foo", 3).put("foo", 1).build())
442        .addEqualityGroup(ImmutableListMultimap.<String, Integer>builder()
443            .put("foo", 2).put("foo", 3).put("foo", 1).build())
444        .addEqualityGroup(ImmutableListMultimap.<String, Integer>builder()
445            .put("bar", 2).put("foo", 3).build())
446        .testEquals();
447  }
448
449  public void testOf() {
450    assertMultimapEquals(
451        ImmutableListMultimap.of("one", 1),
452        "one", 1);
453    assertMultimapEquals(
454        ImmutableListMultimap.of("one", 1, "two", 2),
455        "one", 1, "two", 2);
456    assertMultimapEquals(
457        ImmutableListMultimap.of("one", 1, "two", 2, "three", 3),
458        "one", 1, "two", 2, "three", 3);
459    assertMultimapEquals(
460        ImmutableListMultimap.of("one", 1, "two", 2, "three", 3, "four", 4),
461        "one", 1, "two", 2, "three", 3, "four", 4);
462    assertMultimapEquals(
463        ImmutableListMultimap.of(
464            "one", 1, "two", 2, "three", 3, "four", 4, "five", 5),
465        "one", 1, "two", 2, "three", 3, "four", 4, "five", 5);
466  }
467
468  public void testInverse() {
469    assertEquals(
470        ImmutableListMultimap.<Integer, String>of(),
471        ImmutableListMultimap.<String, Integer>of().inverse());
472    assertEquals(
473        ImmutableListMultimap.of(1, "one"),
474        ImmutableListMultimap.of("one", 1).inverse());
475    assertEquals(
476        ImmutableListMultimap.of(1, "one", 2, "two"),
477        ImmutableListMultimap.of("one", 1, "two", 2).inverse());
478    assertEquals(
479        ImmutableListMultimap.of("of", 'o', "of", 'f', "to", 't', "to", 'o').inverse(),
480        ImmutableListMultimap.of('o', "of", 'f', "of", 't', "to", 'o', "to"));
481    assertEquals(
482        ImmutableListMultimap.of('f', "foo", 'o', "foo", 'o', "foo"),
483        ImmutableListMultimap.of("foo", 'f', "foo", 'o', "foo", 'o').inverse());
484  }
485
486  public void testInverseMinimizesWork() {
487    ImmutableListMultimap<String, Character> multimap =
488        ImmutableListMultimap.<String, Character>builder()
489            .put("foo", 'f')
490            .put("foo", 'o')
491            .put("foo", 'o')
492            .put("poo", 'p')
493            .put("poo", 'o')
494            .put("poo", 'o')
495            .build();
496    assertSame(multimap.inverse(), multimap.inverse());
497    assertSame(multimap, multimap.inverse().inverse());
498  }
499
500  private static <K, V> void assertMultimapEquals(Multimap<K, V> multimap,
501      Object... alternatingKeysAndValues) {
502    assertEquals(multimap.size(), alternatingKeysAndValues.length / 2);
503    int i = 0;
504    for (Entry<K, V> entry : multimap.entries()) {
505      assertEquals(alternatingKeysAndValues[i++], entry.getKey());
506      assertEquals(alternatingKeysAndValues[i++], entry.getValue());
507    }
508  }
509
510  @GwtIncompatible("SerializableTester")
511  public void testSerialization() {
512    Multimap<String, Integer> multimap = createMultimap();
513    SerializableTester.reserializeAndAssert(multimap);
514    assertEquals(multimap.size(),
515        SerializableTester.reserialize(multimap).size());
516    SerializableTester.reserializeAndAssert(multimap.get("foo"));
517    LenientSerializableTester.reserializeAndAssertLenient(multimap.keySet());
518    LenientSerializableTester.reserializeAndAssertLenient(multimap.keys());
519    SerializableTester.reserializeAndAssert(multimap.asMap());
520    Collection<Integer> valuesCopy
521        = SerializableTester.reserialize(multimap.values());
522    assertEquals(HashMultiset.create(multimap.values()),
523        HashMultiset.create(valuesCopy));
524  }
525
526  @GwtIncompatible("SerializableTester")
527  public void testEmptySerialization() {
528    Multimap<String, Integer> multimap = ImmutableListMultimap.of();
529    assertSame(multimap, SerializableTester.reserialize(multimap));
530  }
531}
532