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