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