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 org.junit.contrib.truth.Truth.ASSERT;
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.google.UnmodifiableCollectionTests;
25import com.google.common.testing.EqualsTester;
26import com.google.common.testing.SerializableTester;
27
28import junit.framework.TestCase;
29
30import java.util.Arrays;
31import java.util.Collection;
32import java.util.Collections;
33import java.util.Map.Entry;
34
35/**
36 * Tests for {@link ImmutableListMultimap}.
37 *
38 * @author Jared Levy
39 */
40@GwtCompatible(emulated = true)
41public class ImmutableListMultimapTest extends TestCase {
42
43  public void testBuilder_withImmutableEntry() {
44    ImmutableListMultimap<String, Integer> multimap = new Builder<String, Integer>()
45        .put(Maps.immutableEntry("one", 1))
46        .build();
47    assertEquals(Arrays.asList(1), multimap.get("one"));
48  }
49
50  public void testBuilder_withImmutableEntryAndNullContents() {
51    Builder<String, Integer> builder = new Builder<String, Integer>();
52    try {
53      builder.put(Maps.immutableEntry("one", (Integer) null));
54      fail();
55    } catch (NullPointerException expected) {
56    }
57    try {
58      builder.put(Maps.immutableEntry((String) null, 1));
59      fail();
60    } catch (NullPointerException expected) {
61    }
62  }
63
64  private static class StringHolder {
65    String string;
66  }
67
68  public void testBuilder_withMutableEntry() {
69    ImmutableListMultimap.Builder<String, Integer> builder =
70        new Builder<String, Integer>();
71    final StringHolder holder = new StringHolder();
72    holder.string = "one";
73    Entry<String, Integer> entry = new AbstractMapEntry<String, Integer>() {
74      @Override public String getKey() {
75        return holder.string;
76      }
77      @Override public Integer getValue() {
78        return 1;
79      }
80    };
81
82    builder.put(entry);
83    holder.string = "two";
84    assertEquals(Arrays.asList(1), builder.build().get("one"));
85  }
86
87  public void testBuilderPutAllIterable() {
88    ImmutableListMultimap.Builder<String, Integer> builder
89        = ImmutableListMultimap.builder();
90    builder.putAll("foo", Arrays.asList(1, 2, 3));
91    builder.putAll("bar", Arrays.asList(4, 5));
92    builder.putAll("foo", Arrays.asList(6, 7));
93    Multimap<String, Integer> multimap = builder.build();
94    assertEquals(Arrays.asList(1, 2, 3, 6, 7), multimap.get("foo"));
95    assertEquals(Arrays.asList(4, 5), multimap.get("bar"));
96    assertEquals(7, multimap.size());
97  }
98
99  public void testBuilderPutAllVarargs() {
100    ImmutableListMultimap.Builder<String, Integer> builder
101        = ImmutableListMultimap.builder();
102    builder.putAll("foo", 1, 2, 3);
103    builder.putAll("bar", 4, 5);
104    builder.putAll("foo", 6, 7);
105    Multimap<String, Integer> multimap = builder.build();
106    assertEquals(Arrays.asList(1, 2, 3, 6, 7), multimap.get("foo"));
107    assertEquals(Arrays.asList(4, 5), multimap.get("bar"));
108    assertEquals(7, multimap.size());
109  }
110
111  public void testBuilderPutAllMultimap() {
112    Multimap<String, Integer> toPut = LinkedListMultimap.create();
113    toPut.put("foo", 1);
114    toPut.put("bar", 4);
115    toPut.put("foo", 2);
116    toPut.put("foo", 3);
117    Multimap<String, Integer> moreToPut = LinkedListMultimap.create();
118    moreToPut.put("foo", 6);
119    moreToPut.put("bar", 5);
120    moreToPut.put("foo", 7);
121    ImmutableListMultimap.Builder<String, Integer> builder
122        = ImmutableListMultimap.builder();
123    builder.putAll(toPut);
124    builder.putAll(moreToPut);
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 testBuilderPutAllWithDuplicates() {
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", 1, 6, 7);
137    ImmutableListMultimap<String, Integer> multimap = builder.build();
138    assertEquals(Arrays.asList(1, 2, 3, 1, 6, 7), multimap.get("foo"));
139    assertEquals(Arrays.asList(4, 5), multimap.get("bar"));
140    assertEquals(8, multimap.size());
141  }
142
143  public void testBuilderPutWithDuplicates() {
144    ImmutableListMultimap.Builder<String, Integer> builder
145        = ImmutableListMultimap.builder();
146    builder.putAll("foo", 1, 2, 3);
147    builder.putAll("bar", 4, 5);
148    builder.put("foo", 1);
149    ImmutableListMultimap<String, Integer> multimap = builder.build();
150    assertEquals(Arrays.asList(1, 2, 3, 1), multimap.get("foo"));
151    assertEquals(Arrays.asList(4, 5), multimap.get("bar"));
152    assertEquals(6, multimap.size());
153  }
154
155  public void testBuilderPutAllMultimapWithDuplicates() {
156    Multimap<String, Integer> toPut = LinkedListMultimap.create();
157    toPut.put("foo", 1);
158    toPut.put("bar", 4);
159    toPut.put("foo", 2);
160    toPut.put("foo", 1);
161    toPut.put("bar", 5);
162    Multimap<String, Integer> moreToPut = LinkedListMultimap.create();
163    moreToPut.put("foo", 6);
164    moreToPut.put("bar", 4);
165    moreToPut.put("foo", 7);
166    moreToPut.put("foo", 2);
167    ImmutableListMultimap.Builder<String, Integer> builder
168        = ImmutableListMultimap.builder();
169    builder.putAll(toPut);
170    builder.putAll(moreToPut);
171    Multimap<String, Integer> multimap = builder.build();
172    assertEquals(Arrays.asList(1, 2, 1, 6, 7, 2), multimap.get("foo"));
173    assertEquals(Arrays.asList(4, 5, 4), multimap.get("bar"));
174    assertEquals(9, multimap.size());
175  }
176
177  public void testBuilderPutNullKey() {
178    Multimap<String, Integer> toPut = LinkedListMultimap.create();
179    toPut.put("foo", null);
180    ImmutableListMultimap.Builder<String, Integer> builder
181        = ImmutableListMultimap.builder();
182    try {
183      builder.put(null, 1);
184      fail();
185    } catch (NullPointerException expected) {}
186    try {
187      builder.putAll(null, Arrays.asList(1, 2, 3));
188      fail();
189    } catch (NullPointerException expected) {}
190    try {
191      builder.putAll(null, 1, 2, 3);
192      fail();
193    } catch (NullPointerException expected) {}
194    try {
195      builder.putAll(toPut);
196      fail();
197    } catch (NullPointerException expected) {}
198  }
199
200  public void testBuilderPutNullValue() {
201    Multimap<String, Integer> toPut = LinkedListMultimap.create();
202    toPut.put(null, 1);
203    ImmutableListMultimap.Builder<String, Integer> builder
204        = ImmutableListMultimap.builder();
205    try {
206      builder.put("foo", null);
207      fail();
208    } catch (NullPointerException expected) {}
209    try {
210      builder.putAll("foo", Arrays.asList(1, null, 3));
211      fail();
212    } catch (NullPointerException expected) {}
213    try {
214      builder.putAll("foo", 1, null, 3);
215      fail();
216    } catch (NullPointerException expected) {}
217    try {
218      builder.putAll(toPut);
219      fail();
220    } catch (NullPointerException expected) {}
221  }
222
223  public void testBuilderOrderKeysBy() {
224    ImmutableListMultimap.Builder<String, Integer> builder
225        = ImmutableListMultimap.builder();
226    builder.put("b", 3);
227    builder.put("d", 2);
228    builder.put("a", 5);
229    builder.orderKeysBy(Collections.reverseOrder());
230    builder.put("c", 4);
231    builder.put("a", 2);
232    builder.put("b", 6);
233    ImmutableListMultimap<String, Integer> multimap = builder.build();
234    ASSERT.that(multimap.keySet()).hasContentsInOrder("d", "c", "b", "a");
235    ASSERT.that(multimap.values()).hasContentsInOrder(2, 4, 3, 6, 5, 2);
236    ASSERT.that(multimap.get("a")).hasContentsInOrder(5, 2);
237    ASSERT.that(multimap.get("b")).hasContentsInOrder(3, 6);
238  }
239
240  public void testBuilderOrderValuesBy() {
241    ImmutableListMultimap.Builder<String, Integer> builder
242        = ImmutableListMultimap.builder();
243    builder.put("b", 3);
244    builder.put("d", 2);
245    builder.put("a", 5);
246    builder.orderValuesBy(Collections.reverseOrder());
247    builder.put("c", 4);
248    builder.put("a", 2);
249    builder.put("b", 6);
250    ImmutableListMultimap<String, Integer> multimap = builder.build();
251    ASSERT.that(multimap.keySet()).hasContentsInOrder("b", "d", "a", "c");
252    ASSERT.that(multimap.values()).hasContentsInOrder(6, 3, 2, 5, 2, 4);
253    ASSERT.that(multimap.get("a")).hasContentsInOrder(5, 2);
254    ASSERT.that(multimap.get("b")).hasContentsInOrder(6, 3);
255  }
256
257  public void testBuilderOrderKeysAndValuesBy() {
258    ImmutableListMultimap.Builder<String, Integer> builder
259        = ImmutableListMultimap.builder();
260    builder.put("b", 3);
261    builder.put("d", 2);
262    builder.put("a", 5);
263    builder.orderKeysBy(Collections.reverseOrder());
264    builder.orderValuesBy(Collections.reverseOrder());
265    builder.put("c", 4);
266    builder.put("a", 2);
267    builder.put("b", 6);
268    ImmutableListMultimap<String, Integer> multimap = builder.build();
269    ASSERT.that(multimap.keySet()).hasContentsInOrder("d", "c", "b", "a");
270    ASSERT.that(multimap.values()).hasContentsInOrder(2, 4, 6, 3, 5, 2);
271    ASSERT.that(multimap.get("a")).hasContentsInOrder(5, 2);
272    ASSERT.that(multimap.get("b")).hasContentsInOrder(6, 3);
273  }
274
275  public void testCopyOf() {
276    ArrayListMultimap<String, Integer> input = ArrayListMultimap.create();
277    input.put("foo", 1);
278    input.put("bar", 2);
279    input.put("foo", 3);
280    Multimap<String, Integer> multimap = ImmutableListMultimap.copyOf(input);
281    assertEquals(multimap, input);
282    assertEquals(input, multimap);
283  }
284
285  public void testCopyOfWithDuplicates() {
286    ArrayListMultimap<String, Integer> input = ArrayListMultimap.create();
287    input.put("foo", 1);
288    input.put("bar", 2);
289    input.put("foo", 3);
290    input.put("foo", 1);
291    Multimap<String, Integer> multimap = ImmutableListMultimap.copyOf(input);
292    assertEquals(multimap, input);
293    assertEquals(input, multimap);
294  }
295
296  public void testCopyOfEmpty() {
297    ArrayListMultimap<String, Integer> input = ArrayListMultimap.create();
298    Multimap<String, Integer> multimap = ImmutableListMultimap.copyOf(input);
299    assertEquals(multimap, input);
300    assertEquals(input, multimap);
301  }
302
303  public void testCopyOfImmutableListMultimap() {
304    Multimap<String, Integer> multimap = createMultimap();
305    assertSame(multimap, ImmutableListMultimap.copyOf(multimap));
306  }
307
308  public void testCopyOfNullKey() {
309    ArrayListMultimap<String, Integer> input = ArrayListMultimap.create();
310    input.put(null, 1);
311    try {
312      ImmutableListMultimap.copyOf(input);
313      fail();
314    } catch (NullPointerException expected) {}
315  }
316
317  public void testCopyOfNullValue() {
318    ArrayListMultimap<String, Integer> input = ArrayListMultimap.create();
319    input.putAll("foo", Arrays.asList(1, null, 3));
320    try {
321      ImmutableListMultimap.copyOf(input);
322      fail();
323    } catch (NullPointerException expected) {}
324  }
325
326  public void testEmptyMultimapReads() {
327    Multimap<String, Integer> multimap = ImmutableListMultimap.of();
328    assertFalse(multimap.containsKey("foo"));
329    assertFalse(multimap.containsValue(1));
330    assertFalse(multimap.containsEntry("foo", 1));
331    assertTrue(multimap.entries().isEmpty());
332    assertTrue(multimap.equals(ArrayListMultimap.create()));
333    assertEquals(Collections.emptyList(), multimap.get("foo"));
334    assertEquals(0, multimap.hashCode());
335    assertTrue(multimap.isEmpty());
336    assertEquals(HashMultiset.create(), multimap.keys());
337    assertEquals(Collections.emptySet(), multimap.keySet());
338    assertEquals(0, multimap.size());
339    assertTrue(multimap.values().isEmpty());
340    assertEquals("{}", multimap.toString());
341  }
342
343  public void testEmptyMultimapWrites() {
344    Multimap<String, Integer> multimap = ImmutableListMultimap.of();
345    UnmodifiableCollectionTests.assertMultimapIsUnmodifiable(
346        multimap, "foo", 1);
347  }
348
349  private Multimap<String, Integer> createMultimap() {
350    return ImmutableListMultimap.<String, Integer>builder()
351        .put("foo", 1).put("bar", 2).put("foo", 3).build();
352  }
353
354  public void testMultimapReads() {
355    Multimap<String, Integer> multimap = createMultimap();
356    assertTrue(multimap.containsKey("foo"));
357    assertFalse(multimap.containsKey("cat"));
358    assertTrue(multimap.containsValue(1));
359    assertFalse(multimap.containsValue(5));
360    assertTrue(multimap.containsEntry("foo", 1));
361    assertFalse(multimap.containsEntry("cat", 1));
362    assertFalse(multimap.containsEntry("foo", 5));
363    assertFalse(multimap.entries().isEmpty());
364    assertEquals(3, multimap.size());
365    assertFalse(multimap.isEmpty());
366    assertEquals("{foo=[1, 3], bar=[2]}", multimap.toString());
367  }
368
369  public void testMultimapWrites() {
370    Multimap<String, Integer> multimap = createMultimap();
371    UnmodifiableCollectionTests.assertMultimapIsUnmodifiable(
372        multimap, "bar", 2);
373  }
374
375  public void testMultimapEquals() {
376    Multimap<String, Integer> multimap = createMultimap();
377    Multimap<String, Integer> arrayListMultimap
378        = ArrayListMultimap.create();
379    arrayListMultimap.putAll("foo", Arrays.asList(1, 3));
380    arrayListMultimap.put("bar", 2);
381
382    new EqualsTester()
383        .addEqualityGroup(multimap, createMultimap(), arrayListMultimap,
384            ImmutableListMultimap.<String, Integer>builder()
385                .put("bar", 2).put("foo", 1).put("foo", 3).build())
386        .addEqualityGroup(ImmutableListMultimap.<String, Integer>builder()
387            .put("bar", 2).put("foo", 3).put("foo", 1).build())
388        .addEqualityGroup(ImmutableListMultimap.<String, Integer>builder()
389            .put("foo", 2).put("foo", 3).put("foo", 1).build())
390        .addEqualityGroup(ImmutableListMultimap.<String, Integer>builder()
391            .put("bar", 2).put("foo", 3).build())
392        .testEquals();
393  }
394
395  public void testOf() {
396    assertMultimapEquals(
397        ImmutableListMultimap.of("one", 1),
398        "one", 1);
399    assertMultimapEquals(
400        ImmutableListMultimap.of("one", 1, "two", 2),
401        "one", 1, "two", 2);
402    assertMultimapEquals(
403        ImmutableListMultimap.of("one", 1, "two", 2, "three", 3),
404        "one", 1, "two", 2, "three", 3);
405    assertMultimapEquals(
406        ImmutableListMultimap.of("one", 1, "two", 2, "three", 3, "four", 4),
407        "one", 1, "two", 2, "three", 3, "four", 4);
408    assertMultimapEquals(
409        ImmutableListMultimap.of(
410            "one", 1, "two", 2, "three", 3, "four", 4, "five", 5),
411        "one", 1, "two", 2, "three", 3, "four", 4, "five", 5);
412  }
413
414  public void testInverse() {
415    assertEquals(
416        ImmutableListMultimap.<Integer, String>of(),
417        ImmutableListMultimap.<String, Integer>of().inverse());
418    assertEquals(
419        ImmutableListMultimap.of(1, "one"),
420        ImmutableListMultimap.of("one", 1).inverse());
421    assertEquals(
422        ImmutableListMultimap.of(1, "one", 2, "two"),
423        ImmutableListMultimap.of("one", 1, "two", 2).inverse());
424    assertEquals(
425        ImmutableListMultimap.of("of", 'o', "of", 'f', "to", 't', "to", 'o').inverse(),
426        ImmutableListMultimap.of('o', "of", 'f', "of", 't', "to", 'o', "to"));
427    assertEquals(
428        ImmutableListMultimap.of('f', "foo", 'o', "foo", 'o', "foo"),
429        ImmutableListMultimap.of("foo", 'f', "foo", 'o', "foo", 'o').inverse());
430  }
431
432  public void testInverseMinimizesWork() {
433    ImmutableListMultimap<String, Character> multimap =
434        ImmutableListMultimap.<String, Character>builder()
435            .put("foo", 'f')
436            .put("foo", 'o')
437            .put("foo", 'o')
438            .put("poo", 'p')
439            .put("poo", 'o')
440            .put("poo", 'o')
441            .build();
442    assertSame(multimap.inverse(), multimap.inverse());
443    assertSame(multimap, multimap.inverse().inverse());
444  }
445
446  private static <K, V> void assertMultimapEquals(Multimap<K, V> multimap,
447      Object... alternatingKeysAndValues) {
448    assertEquals(multimap.size(), alternatingKeysAndValues.length / 2);
449    int i = 0;
450    for (Entry<K, V> entry : multimap.entries()) {
451      assertEquals(alternatingKeysAndValues[i++], entry.getKey());
452      assertEquals(alternatingKeysAndValues[i++], entry.getValue());
453    }
454  }
455
456  @GwtIncompatible("SerializableTester")
457  public void testSerialization() {
458    Multimap<String, Integer> multimap = createMultimap();
459    SerializableTester.reserializeAndAssert(multimap);
460    assertEquals(multimap.size(),
461        SerializableTester.reserialize(multimap).size());
462    SerializableTester.reserializeAndAssert(multimap.get("foo"));
463    LenientSerializableTester.reserializeAndAssertLenient(multimap.keySet());
464    SerializableTester.reserializeAndAssert(multimap.keys());
465    SerializableTester.reserializeAndAssert(multimap.asMap());
466    Collection<Integer> valuesCopy
467        = SerializableTester.reserialize(multimap.values());
468    assertEquals(HashMultiset.create(multimap.values()),
469        HashMultiset.create(valuesCopy));
470  }
471
472  @GwtIncompatible("SerializableTester")
473  public void testEmptySerialization() {
474    Multimap<String, Integer> multimap = ImmutableListMultimap.of();
475    assertSame(multimap, SerializableTester.reserialize(multimap));
476  }
477}
478