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