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