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