1 /* 2 * Copyright (C) 2008 The Guava Authors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.google.common.collect; 18 19 import static com.google.common.testing.SerializableTester.reserialize; 20 import static com.google.common.truth.Truth.assertThat; 21 import static com.google.common.truth.Truth.assertWithMessage; 22 23 import com.google.common.annotations.GwtCompatible; 24 import com.google.common.annotations.GwtIncompatible; 25 import com.google.common.base.Joiner; 26 import com.google.common.collect.ImmutableMap.Builder; 27 import com.google.common.collect.testing.CollectionTestSuiteBuilder; 28 import com.google.common.collect.testing.ListTestSuiteBuilder; 29 import com.google.common.collect.testing.MapInterfaceTest; 30 import com.google.common.collect.testing.MapTestSuiteBuilder; 31 import com.google.common.collect.testing.MinimalSet; 32 import com.google.common.collect.testing.SampleElements.Colliders; 33 import com.google.common.collect.testing.SampleElements.Unhashables; 34 import com.google.common.collect.testing.UnhashableObject; 35 import com.google.common.collect.testing.features.CollectionFeature; 36 import com.google.common.collect.testing.features.CollectionSize; 37 import com.google.common.collect.testing.features.MapFeature; 38 import com.google.common.collect.testing.google.MapGenerators.ImmutableMapCopyOfEntriesGenerator; 39 import com.google.common.collect.testing.google.MapGenerators.ImmutableMapCopyOfEnumMapGenerator; 40 import com.google.common.collect.testing.google.MapGenerators.ImmutableMapCopyOfGenerator; 41 import com.google.common.collect.testing.google.MapGenerators.ImmutableMapEntryListGenerator; 42 import com.google.common.collect.testing.google.MapGenerators.ImmutableMapGenerator; 43 import com.google.common.collect.testing.google.MapGenerators.ImmutableMapKeyListGenerator; 44 import com.google.common.collect.testing.google.MapGenerators.ImmutableMapUnhashableValuesGenerator; 45 import com.google.common.collect.testing.google.MapGenerators.ImmutableMapValueListGenerator; 46 import com.google.common.collect.testing.google.MapGenerators.ImmutableMapValuesAsSingletonSetGenerator; 47 import com.google.common.testing.EqualsTester; 48 import com.google.common.testing.NullPointerTester; 49 import com.google.common.testing.SerializableTester; 50 import java.io.ByteArrayOutputStream; 51 import java.io.ObjectOutputStream; 52 import java.io.Serializable; 53 import java.util.AbstractMap; 54 import java.util.Collection; 55 import java.util.Collections; 56 import java.util.LinkedHashMap; 57 import java.util.Map; 58 import java.util.Map.Entry; 59 import java.util.Set; 60 import java.util.regex.Matcher; 61 import java.util.regex.Pattern; 62 import junit.framework.Test; 63 import junit.framework.TestCase; 64 import junit.framework.TestSuite; 65 66 /** 67 * Tests for {@link ImmutableMap}. 68 * 69 * @author Kevin Bourrillion 70 * @author Jesse Wilson 71 */ 72 @GwtCompatible(emulated = true) 73 @SuppressWarnings("AlwaysThrows") 74 public class ImmutableMapTest extends TestCase { 75 76 @GwtIncompatible // suite suite()77 public static Test suite() { 78 TestSuite suite = new TestSuite(); 79 suite.addTestSuite(ImmutableMapTest.class); 80 81 suite.addTest( 82 MapTestSuiteBuilder.using(new ImmutableMapGenerator()) 83 .withFeatures( 84 CollectionSize.ANY, 85 CollectionFeature.SERIALIZABLE_INCLUDING_VIEWS, 86 CollectionFeature.KNOWN_ORDER, 87 MapFeature.REJECTS_DUPLICATES_AT_CREATION, 88 CollectionFeature.ALLOWS_NULL_QUERIES) 89 .named("ImmutableMap") 90 .createTestSuite()); 91 92 suite.addTest( 93 MapTestSuiteBuilder.using(new ImmutableMapCopyOfGenerator()) 94 .withFeatures( 95 CollectionSize.ANY, 96 CollectionFeature.SERIALIZABLE_INCLUDING_VIEWS, 97 CollectionFeature.KNOWN_ORDER, 98 CollectionFeature.ALLOWS_NULL_QUERIES) 99 .named("ImmutableMap.copyOf[Map]") 100 .createTestSuite()); 101 102 suite.addTest( 103 MapTestSuiteBuilder.using(new ImmutableMapCopyOfEntriesGenerator()) 104 .withFeatures( 105 CollectionSize.ANY, 106 MapFeature.REJECTS_DUPLICATES_AT_CREATION, 107 CollectionFeature.SERIALIZABLE_INCLUDING_VIEWS, 108 CollectionFeature.KNOWN_ORDER, 109 CollectionFeature.ALLOWS_NULL_QUERIES) 110 .named("ImmutableMap.copyOf[Iterable<Entry>]") 111 .createTestSuite()); 112 113 suite.addTest( 114 MapTestSuiteBuilder.using(new ImmutableMapCopyOfEnumMapGenerator()) 115 .withFeatures( 116 CollectionSize.ANY, 117 CollectionFeature.SERIALIZABLE_INCLUDING_VIEWS, 118 CollectionFeature.KNOWN_ORDER, 119 CollectionFeature.ALLOWS_NULL_QUERIES) 120 .named("ImmutableMap.copyOf[EnumMap]") 121 .createTestSuite()); 122 123 suite.addTest( 124 MapTestSuiteBuilder.using(new ImmutableMapValuesAsSingletonSetGenerator()) 125 .withFeatures( 126 CollectionSize.ANY, 127 MapFeature.REJECTS_DUPLICATES_AT_CREATION, 128 CollectionFeature.KNOWN_ORDER, 129 CollectionFeature.ALLOWS_NULL_QUERIES) 130 .named("ImmutableMap.asMultimap.asMap") 131 .createTestSuite()); 132 133 suite.addTest( 134 CollectionTestSuiteBuilder.using(new ImmutableMapUnhashableValuesGenerator()) 135 .withFeatures( 136 CollectionSize.ANY, 137 CollectionFeature.KNOWN_ORDER, 138 CollectionFeature.ALLOWS_NULL_QUERIES) 139 .named("ImmutableMap.values, unhashable") 140 .createTestSuite()); 141 142 suite.addTest( 143 ListTestSuiteBuilder.using(new ImmutableMapKeyListGenerator()) 144 .named("ImmutableMap.keySet.asList") 145 .withFeatures( 146 CollectionSize.ANY, 147 CollectionFeature.SERIALIZABLE, 148 CollectionFeature.REJECTS_DUPLICATES_AT_CREATION, 149 CollectionFeature.ALLOWS_NULL_QUERIES) 150 .createTestSuite()); 151 152 suite.addTest( 153 ListTestSuiteBuilder.using(new ImmutableMapEntryListGenerator()) 154 .named("ImmutableMap.entrySet.asList") 155 .withFeatures( 156 CollectionSize.ANY, 157 CollectionFeature.SERIALIZABLE, 158 CollectionFeature.REJECTS_DUPLICATES_AT_CREATION, 159 CollectionFeature.ALLOWS_NULL_QUERIES) 160 .createTestSuite()); 161 162 suite.addTest( 163 ListTestSuiteBuilder.using(new ImmutableMapValueListGenerator()) 164 .named("ImmutableMap.values.asList") 165 .withFeatures( 166 CollectionSize.ANY, 167 CollectionFeature.SERIALIZABLE, 168 CollectionFeature.ALLOWS_NULL_QUERIES) 169 .createTestSuite()); 170 171 return suite; 172 } 173 174 public abstract static class AbstractMapTests<K, V> extends MapInterfaceTest<K, V> { AbstractMapTests()175 public AbstractMapTests() { 176 super(false, false, false, false, false); 177 } 178 179 @Override makeEmptyMap()180 protected Map<K, V> makeEmptyMap() { 181 throw new UnsupportedOperationException(); 182 } 183 184 private static final Joiner JOINER = Joiner.on(", "); 185 186 @Override assertMoreInvariants(Map<K, V> map)187 protected void assertMoreInvariants(Map<K, V> map) { 188 // TODO: can these be moved to MapInterfaceTest? 189 for (Entry<K, V> entry : map.entrySet()) { 190 assertEquals(entry.getKey() + "=" + entry.getValue(), entry.toString()); 191 } 192 193 assertEquals("{" + JOINER.join(map.entrySet()) + "}", map.toString()); 194 assertEquals("[" + JOINER.join(map.entrySet()) + "]", map.entrySet().toString()); 195 assertEquals("[" + JOINER.join(map.keySet()) + "]", map.keySet().toString()); 196 assertEquals("[" + JOINER.join(map.values()) + "]", map.values().toString()); 197 198 assertEquals(MinimalSet.from(map.entrySet()), map.entrySet()); 199 assertEquals(Sets.newHashSet(map.keySet()), map.keySet()); 200 } 201 } 202 203 public static class MapTests extends AbstractMapTests<String, Integer> { 204 @Override makeEmptyMap()205 protected Map<String, Integer> makeEmptyMap() { 206 return ImmutableMap.of(); 207 } 208 209 @Override makePopulatedMap()210 protected Map<String, Integer> makePopulatedMap() { 211 return ImmutableMap.of("one", 1, "two", 2, "three", 3); 212 } 213 214 @Override getKeyNotInPopulatedMap()215 protected String getKeyNotInPopulatedMap() { 216 return "minus one"; 217 } 218 219 @Override getValueNotInPopulatedMap()220 protected Integer getValueNotInPopulatedMap() { 221 return -1; 222 } 223 } 224 225 public static class SingletonMapTests extends AbstractMapTests<String, Integer> { 226 @Override makePopulatedMap()227 protected Map<String, Integer> makePopulatedMap() { 228 return ImmutableMap.of("one", 1); 229 } 230 231 @Override getKeyNotInPopulatedMap()232 protected String getKeyNotInPopulatedMap() { 233 return "minus one"; 234 } 235 236 @Override getValueNotInPopulatedMap()237 protected Integer getValueNotInPopulatedMap() { 238 return -1; 239 } 240 } 241 242 @GwtIncompatible // SerializableTester 243 public static class ReserializedMapTests extends AbstractMapTests<String, Integer> { 244 @Override makePopulatedMap()245 protected Map<String, Integer> makePopulatedMap() { 246 return SerializableTester.reserialize(ImmutableMap.of("one", 1, "two", 2, "three", 3)); 247 } 248 249 @Override getKeyNotInPopulatedMap()250 protected String getKeyNotInPopulatedMap() { 251 return "minus one"; 252 } 253 254 @Override getValueNotInPopulatedMap()255 protected Integer getValueNotInPopulatedMap() { 256 return -1; 257 } 258 } 259 260 public static class MapTestsWithBadHashes extends AbstractMapTests<Object, Integer> { 261 262 @Override makeEmptyMap()263 protected Map<Object, Integer> makeEmptyMap() { 264 throw new UnsupportedOperationException(); 265 } 266 267 @Override makePopulatedMap()268 protected Map<Object, Integer> makePopulatedMap() { 269 Colliders colliders = new Colliders(); 270 return ImmutableMap.of( 271 colliders.e0(), 0, 272 colliders.e1(), 1, 273 colliders.e2(), 2, 274 colliders.e3(), 3); 275 } 276 277 @Override getKeyNotInPopulatedMap()278 protected Object getKeyNotInPopulatedMap() { 279 return new Colliders().e4(); 280 } 281 282 @Override getValueNotInPopulatedMap()283 protected Integer getValueNotInPopulatedMap() { 284 return 4; 285 } 286 } 287 288 @GwtIncompatible // GWT's ImmutableMap emulation is backed by java.util.HashMap. 289 public static class MapTestsWithUnhashableValues 290 extends AbstractMapTests<Integer, UnhashableObject> { 291 @Override makeEmptyMap()292 protected Map<Integer, UnhashableObject> makeEmptyMap() { 293 return ImmutableMap.of(); 294 } 295 296 @Override makePopulatedMap()297 protected Map<Integer, UnhashableObject> makePopulatedMap() { 298 Unhashables unhashables = new Unhashables(); 299 return ImmutableMap.of(0, unhashables.e0(), 1, unhashables.e1(), 2, unhashables.e2()); 300 } 301 302 @Override getKeyNotInPopulatedMap()303 protected Integer getKeyNotInPopulatedMap() { 304 return 3; 305 } 306 307 @Override getValueNotInPopulatedMap()308 protected UnhashableObject getValueNotInPopulatedMap() { 309 return new Unhashables().e3(); 310 } 311 } 312 313 @GwtIncompatible // GWT's ImmutableMap emulation is backed by java.util.HashMap. 314 public static class MapTestsWithSingletonUnhashableValue extends MapTestsWithUnhashableValues { 315 @Override makePopulatedMap()316 protected Map<Integer, UnhashableObject> makePopulatedMap() { 317 Unhashables unhashables = new Unhashables(); 318 return ImmutableMap.of(0, unhashables.e0()); 319 } 320 } 321 322 public static class CreationTests extends TestCase { testEmptyBuilder()323 public void testEmptyBuilder() { 324 ImmutableMap<String, Integer> map = new Builder<String, Integer>().buildOrThrow(); 325 assertEquals(Collections.<String, Integer>emptyMap(), map); 326 } 327 testSingletonBuilder()328 public void testSingletonBuilder() { 329 ImmutableMap<String, Integer> map = 330 new Builder<String, Integer>().put("one", 1).buildOrThrow(); 331 assertMapEquals(map, "one", 1); 332 } 333 testBuilder()334 public void testBuilder() { 335 ImmutableMap<String, Integer> map = 336 new Builder<String, Integer>() 337 .put("one", 1) 338 .put("two", 2) 339 .put("three", 3) 340 .put("four", 4) 341 .put("five", 5) 342 .buildOrThrow(); 343 assertMapEquals(map, "one", 1, "two", 2, "three", 3, "four", 4, "five", 5); 344 } 345 346 @GwtIncompatible testBuilderExactlySizedReusesArray()347 public void testBuilderExactlySizedReusesArray() { 348 ImmutableMap.Builder<Integer, Integer> builder = ImmutableMap.builderWithExpectedSize(10); 349 Object[] builderArray = builder.alternatingKeysAndValues; 350 for (int i = 0; i < 10; i++) { 351 builder.put(i, i); 352 } 353 Object[] builderArrayAfterPuts = builder.alternatingKeysAndValues; 354 RegularImmutableMap<Integer, Integer> map = 355 (RegularImmutableMap<Integer, Integer>) builder.buildOrThrow(); 356 Object[] mapInternalArray = map.alternatingKeysAndValues; 357 assertSame(builderArray, builderArrayAfterPuts); 358 assertSame(builderArray, mapInternalArray); 359 } 360 testBuilder_orderEntriesByValue()361 public void testBuilder_orderEntriesByValue() { 362 ImmutableMap<String, Integer> map = 363 new Builder<String, Integer>() 364 .orderEntriesByValue(Ordering.natural()) 365 .put("three", 3) 366 .put("one", 1) 367 .put("five", 5) 368 .put("four", 4) 369 .put("two", 2) 370 .buildOrThrow(); 371 assertMapEquals(map, "one", 1, "two", 2, "three", 3, "four", 4, "five", 5); 372 } 373 testBuilder_orderEntriesByValueAfterExactSizeBuild()374 public void testBuilder_orderEntriesByValueAfterExactSizeBuild() { 375 Builder<String, Integer> builder = 376 new Builder<String, Integer>(2).put("four", 4).put("one", 1); 377 ImmutableMap<String, Integer> keyOrdered = builder.buildOrThrow(); 378 ImmutableMap<String, Integer> valueOrdered = 379 builder.orderEntriesByValue(Ordering.natural()).buildOrThrow(); 380 assertMapEquals(keyOrdered, "four", 4, "one", 1); 381 assertMapEquals(valueOrdered, "one", 1, "four", 4); 382 } 383 testBuilder_orderEntriesByValue_usedTwiceFails()384 public void testBuilder_orderEntriesByValue_usedTwiceFails() { 385 ImmutableMap.Builder<String, Integer> builder = 386 new Builder<String, Integer>().orderEntriesByValue(Ordering.natural()); 387 try { 388 builder.orderEntriesByValue(Ordering.natural()); 389 fail("Expected IllegalStateException"); 390 } catch (IllegalStateException expected) { 391 } 392 } 393 394 @GwtIncompatible // we haven't implemented this testBuilder_orderEntriesByValue_keepingLast()395 public void testBuilder_orderEntriesByValue_keepingLast() { 396 ImmutableMap.Builder<String, Integer> builder = 397 new Builder<String, Integer>() 398 .orderEntriesByValue(Ordering.natural()) 399 .put("three", 3) 400 .put("one", 1) 401 .put("five", 5) 402 .put("four", 3) 403 .put("four", 5) 404 .put("four", 4) // this should win because it's last 405 .put("two", 2); 406 assertMapEquals( 407 builder.buildKeepingLast(), "one", 1, "two", 2, "three", 3, "four", 4, "five", 5); 408 try { 409 builder.buildOrThrow(); 410 fail("Expected exception from duplicate keys"); 411 } catch (IllegalArgumentException expected) { 412 } 413 } 414 415 @GwtIncompatible // we haven't implemented this testBuilder_orderEntriesByValue_keepingLast_builderSizeFieldPreserved()416 public void testBuilder_orderEntriesByValue_keepingLast_builderSizeFieldPreserved() { 417 ImmutableMap.Builder<String, Integer> builder = 418 new Builder<String, Integer>() 419 .orderEntriesByValue(Ordering.natural()) 420 .put("one", 1) 421 .put("one", 1); 422 assertMapEquals(builder.buildKeepingLast(), "one", 1); 423 try { 424 builder.buildOrThrow(); 425 fail("Expected exception from duplicate keys"); 426 } catch (IllegalArgumentException expected) { 427 } 428 } 429 testBuilder_withImmutableEntry()430 public void testBuilder_withImmutableEntry() { 431 ImmutableMap<String, Integer> map = 432 new Builder<String, Integer>().put(Maps.immutableEntry("one", 1)).buildOrThrow(); 433 assertMapEquals(map, "one", 1); 434 } 435 testBuilder_withImmutableEntryAndNullContents()436 public void testBuilder_withImmutableEntryAndNullContents() { 437 Builder<String, Integer> builder = new Builder<>(); 438 try { 439 builder.put(Maps.immutableEntry("one", (Integer) null)); 440 fail(); 441 } catch (NullPointerException expected) { 442 } 443 try { 444 builder.put(Maps.immutableEntry((String) null, 1)); 445 fail(); 446 } catch (NullPointerException expected) { 447 } 448 } 449 450 private static class StringHolder { 451 String string; 452 } 453 testBuilder_withMutableEntry()454 public void testBuilder_withMutableEntry() { 455 ImmutableMap.Builder<String, Integer> builder = new Builder<>(); 456 final StringHolder holder = new StringHolder(); 457 holder.string = "one"; 458 Entry<String, Integer> entry = 459 new AbstractMapEntry<String, Integer>() { 460 @Override 461 public String getKey() { 462 return holder.string; 463 } 464 465 @Override 466 public Integer getValue() { 467 return 1; 468 } 469 }; 470 471 builder.put(entry); 472 holder.string = "two"; 473 assertMapEquals(builder.buildOrThrow(), "one", 1); 474 } 475 testBuilderPutAllWithEmptyMap()476 public void testBuilderPutAllWithEmptyMap() { 477 ImmutableMap<String, Integer> map = 478 new Builder<String, Integer>() 479 .putAll(Collections.<String, Integer>emptyMap()) 480 .buildOrThrow(); 481 assertEquals(Collections.<String, Integer>emptyMap(), map); 482 } 483 testBuilderPutAll()484 public void testBuilderPutAll() { 485 Map<String, Integer> toPut = new LinkedHashMap<>(); 486 toPut.put("one", 1); 487 toPut.put("two", 2); 488 toPut.put("three", 3); 489 Map<String, Integer> moreToPut = new LinkedHashMap<>(); 490 moreToPut.put("four", 4); 491 moreToPut.put("five", 5); 492 493 ImmutableMap<String, Integer> map = 494 new Builder<String, Integer>().putAll(toPut).putAll(moreToPut).buildOrThrow(); 495 assertMapEquals(map, "one", 1, "two", 2, "three", 3, "four", 4, "five", 5); 496 } 497 testBuilderReuse()498 public void testBuilderReuse() { 499 Builder<String, Integer> builder = new Builder<>(); 500 ImmutableMap<String, Integer> mapOne = builder.put("one", 1).put("two", 2).buildOrThrow(); 501 ImmutableMap<String, Integer> mapTwo = builder.put("three", 3).put("four", 4).buildOrThrow(); 502 503 assertMapEquals(mapOne, "one", 1, "two", 2); 504 assertMapEquals(mapTwo, "one", 1, "two", 2, "three", 3, "four", 4); 505 } 506 testBuilderPutNullKeyFailsAtomically()507 public void testBuilderPutNullKeyFailsAtomically() { 508 Builder<String, Integer> builder = new Builder<>(); 509 try { 510 builder.put(null, 1); 511 fail(); 512 } catch (NullPointerException expected) { 513 } 514 builder.put("foo", 2); 515 assertMapEquals(builder.buildOrThrow(), "foo", 2); 516 } 517 testBuilderPutImmutableEntryWithNullKeyFailsAtomically()518 public void testBuilderPutImmutableEntryWithNullKeyFailsAtomically() { 519 Builder<String, Integer> builder = new Builder<>(); 520 try { 521 builder.put(Maps.immutableEntry((String) null, 1)); 522 fail(); 523 } catch (NullPointerException expected) { 524 } 525 builder.put("foo", 2); 526 assertMapEquals(builder.buildOrThrow(), "foo", 2); 527 } 528 529 // for GWT compatibility 530 static class SimpleEntry<K, V> extends AbstractMapEntry<K, V> { 531 public K key; 532 public V value; 533 SimpleEntry(K key, V value)534 SimpleEntry(K key, V value) { 535 this.key = key; 536 this.value = value; 537 } 538 539 @Override getKey()540 public K getKey() { 541 return key; 542 } 543 544 @Override getValue()545 public V getValue() { 546 return value; 547 } 548 } 549 testBuilderPutMutableEntryWithNullKeyFailsAtomically()550 public void testBuilderPutMutableEntryWithNullKeyFailsAtomically() { 551 Builder<String, Integer> builder = new Builder<>(); 552 try { 553 builder.put(new SimpleEntry<String, Integer>(null, 1)); 554 fail(); 555 } catch (NullPointerException expected) { 556 } 557 builder.put("foo", 2); 558 assertMapEquals(builder.buildOrThrow(), "foo", 2); 559 } 560 testBuilderPutNullKey()561 public void testBuilderPutNullKey() { 562 Builder<String, Integer> builder = new Builder<>(); 563 try { 564 builder.put(null, 1); 565 fail(); 566 } catch (NullPointerException expected) { 567 } 568 } 569 testBuilderPutNullValue()570 public void testBuilderPutNullValue() { 571 Builder<String, Integer> builder = new Builder<>(); 572 try { 573 builder.put("one", null); 574 fail(); 575 } catch (NullPointerException expected) { 576 } 577 } 578 testBuilderPutNullKeyViaPutAll()579 public void testBuilderPutNullKeyViaPutAll() { 580 Builder<String, Integer> builder = new Builder<>(); 581 try { 582 builder.putAll(Collections.<String, Integer>singletonMap(null, 1)); 583 fail(); 584 } catch (NullPointerException expected) { 585 } 586 } 587 testBuilderPutNullValueViaPutAll()588 public void testBuilderPutNullValueViaPutAll() { 589 Builder<String, Integer> builder = new Builder<>(); 590 try { 591 builder.putAll(Collections.<String, Integer>singletonMap("one", null)); 592 fail(); 593 } catch (NullPointerException expected) { 594 } 595 } 596 testPuttingTheSameKeyTwiceThrowsOnBuild()597 public void testPuttingTheSameKeyTwiceThrowsOnBuild() { 598 Builder<String, Integer> builder = 599 new Builder<String, Integer>() 600 .put("one", 1) 601 .put("one", 1); // throwing on this line might be better but it's too late to change 602 603 try { 604 builder.buildOrThrow(); 605 fail(); 606 } catch (IllegalArgumentException expected) { 607 } 608 } 609 testBuildKeepingLast_allowsOverwrite()610 public void testBuildKeepingLast_allowsOverwrite() { 611 Builder<Integer, String> builder = 612 new Builder<Integer, String>() 613 .put(1, "un") 614 .put(2, "deux") 615 .put(70, "soixante-dix") 616 .put(70, "septante") 617 .put(70, "seventy") 618 .put(1, "one") 619 .put(2, "two"); 620 ImmutableMap<Integer, String> map = builder.buildKeepingLast(); 621 assertMapEquals(map, 1, "one", 2, "two", 70, "seventy"); 622 } 623 testBuildKeepingLast_smallTableSameHash()624 public void testBuildKeepingLast_smallTableSameHash() { 625 String key1 = "QED"; 626 String key2 = "R&D"; 627 assertThat(key1.hashCode()).isEqualTo(key2.hashCode()); 628 ImmutableMap<String, Integer> map = 629 ImmutableMap.<String, Integer>builder() 630 .put(key1, 1) 631 .put(key2, 2) 632 .put(key1, 3) 633 .put(key2, 4) 634 .buildKeepingLast(); 635 assertMapEquals(map, key1, 3, key2, 4); 636 } 637 638 // The java7 branch has different code depending on whether the entry indexes fit in a byte, 639 // short, or int. The small table in testBuildKeepingLast_allowsOverwrite will test the byte 640 // case. This method tests the short case. testBuildKeepingLast_shortTable()641 public void testBuildKeepingLast_shortTable() { 642 Builder<Integer, String> builder = ImmutableMap.builder(); 643 Map<Integer, String> expected = new LinkedHashMap<>(); 644 for (int i = 0; i < 1000; i++) { 645 // Truncate to even key, so we have put(0, "0") then put(0, "1"). Half the entries are 646 // duplicates. 647 Integer key = i & ~1; 648 String value = String.valueOf(i); 649 builder.put(key, value); 650 expected.put(key, value); 651 } 652 ImmutableMap<Integer, String> map = builder.buildKeepingLast(); 653 assertThat(map).hasSize(500); 654 assertThat(map).containsExactlyEntriesIn(expected).inOrder(); 655 } 656 657 // This method tests the int case. testBuildKeepingLast_bigTable()658 public void testBuildKeepingLast_bigTable() { 659 Builder<Integer, String> builder = ImmutableMap.builder(); 660 Map<Integer, String> expected = new LinkedHashMap<>(); 661 for (int i = 0; i < 200_000; i++) { 662 // Truncate to even key, so we have put(0, "0") then put(0, "1"). Half the entries are 663 // duplicates. 664 Integer key = i & ~1; 665 String value = String.valueOf(i); 666 builder.put(key, value); 667 expected.put(key, value); 668 } 669 ImmutableMap<Integer, String> map = builder.buildKeepingLast(); 670 assertThat(map).hasSize(100_000); 671 assertThat(map).containsExactlyEntriesIn(expected).inOrder(); 672 } 673 674 @GwtIncompatible // Pattern, Matcher testBuilder_keepingLast_thenOrThrow()675 public void testBuilder_keepingLast_thenOrThrow() { 676 ImmutableMap.Builder<String, Integer> builder = 677 new Builder<String, Integer>() 678 .put("three", 3) 679 .put("one", 1) 680 .put("five", 5) 681 .put("four", 3) 682 .put("four", 5) 683 .put("four", 4) // this should win because it's last 684 .put("two", 2); 685 assertMapEquals( 686 builder.buildKeepingLast(), "three", 3, "one", 1, "five", 5, "four", 4, "two", 2); 687 try { 688 builder.buildOrThrow(); 689 fail("Expected exception from duplicate keys"); 690 } catch (IllegalArgumentException expected) { 691 // We don't really care which values the exception message contains, but they should be 692 // different from each other. If buildKeepingLast() collapsed duplicates, that might end up 693 // not being true. 694 Pattern pattern = 695 Pattern.compile("Multiple entries with same key: four=(.*) and four=(.*)"); 696 assertThat(expected).hasMessageThat().matches(pattern); 697 Matcher matcher = pattern.matcher(expected.getMessage()); 698 assertThat(matcher.matches()).isTrue(); 699 assertThat(matcher.group(1)).isNotEqualTo(matcher.group(2)); 700 } 701 } 702 testOf()703 public void testOf() { 704 assertMapEquals(ImmutableMap.of("one", 1), "one", 1); 705 assertMapEquals(ImmutableMap.of("one", 1, "two", 2), "one", 1, "two", 2); 706 assertMapEquals( 707 ImmutableMap.of("one", 1, "two", 2, "three", 3), "one", 1, "two", 2, "three", 3); 708 assertMapEquals( 709 ImmutableMap.of("one", 1, "two", 2, "three", 3, "four", 4), 710 "one", 711 1, 712 "two", 713 2, 714 "three", 715 3, 716 "four", 717 4); 718 assertMapEquals( 719 ImmutableMap.of("one", 1, "two", 2, "three", 3, "four", 4, "five", 5), 720 "one", 721 1, 722 "two", 723 2, 724 "three", 725 3, 726 "four", 727 4, 728 "five", 729 5); 730 assertMapEquals( 731 ImmutableMap.of( 732 "one", 1, 733 "two", 2, 734 "three", 3, 735 "four", 4, 736 "five", 5, 737 "six", 6), 738 "one", 739 1, 740 "two", 741 2, 742 "three", 743 3, 744 "four", 745 4, 746 "five", 747 5, 748 "six", 749 6); 750 assertMapEquals( 751 ImmutableMap.of( 752 "one", 1, 753 "two", 2, 754 "three", 3, 755 "four", 4, 756 "five", 5, 757 "six", 6, 758 "seven", 7), 759 "one", 760 1, 761 "two", 762 2, 763 "three", 764 3, 765 "four", 766 4, 767 "five", 768 5, 769 "six", 770 6, 771 "seven", 772 7); 773 assertMapEquals( 774 ImmutableMap.of( 775 "one", 1, 776 "two", 2, 777 "three", 3, 778 "four", 4, 779 "five", 5, 780 "six", 6, 781 "seven", 7, 782 "eight", 8), 783 "one", 784 1, 785 "two", 786 2, 787 "three", 788 3, 789 "four", 790 4, 791 "five", 792 5, 793 "six", 794 6, 795 "seven", 796 7, 797 "eight", 798 8); 799 assertMapEquals( 800 ImmutableMap.of( 801 "one", 1, 802 "two", 2, 803 "three", 3, 804 "four", 4, 805 "five", 5, 806 "six", 6, 807 "seven", 7, 808 "eight", 8, 809 "nine", 9), 810 "one", 811 1, 812 "two", 813 2, 814 "three", 815 3, 816 "four", 817 4, 818 "five", 819 5, 820 "six", 821 6, 822 "seven", 823 7, 824 "eight", 825 8, 826 "nine", 827 9); 828 assertMapEquals( 829 ImmutableMap.of( 830 "one", 1, 831 "two", 2, 832 "three", 3, 833 "four", 4, 834 "five", 5, 835 "six", 6, 836 "seven", 7, 837 "eight", 8, 838 "nine", 9, 839 "ten", 10), 840 "one", 841 1, 842 "two", 843 2, 844 "three", 845 3, 846 "four", 847 4, 848 "five", 849 5, 850 "six", 851 6, 852 "seven", 853 7, 854 "eight", 855 8, 856 "nine", 857 9, 858 "ten", 859 10); 860 } 861 testOfNullKey()862 public void testOfNullKey() { 863 try { 864 ImmutableMap.of(null, 1); 865 fail(); 866 } catch (NullPointerException expected) { 867 } 868 869 try { 870 ImmutableMap.of("one", 1, null, 2); 871 fail(); 872 } catch (NullPointerException expected) { 873 } 874 } 875 testOfNullValue()876 public void testOfNullValue() { 877 try { 878 ImmutableMap.of("one", null); 879 fail(); 880 } catch (NullPointerException expected) { 881 } 882 883 try { 884 ImmutableMap.of("one", 1, "two", null); 885 fail(); 886 } catch (NullPointerException expected) { 887 } 888 } 889 testOfWithDuplicateKey()890 public void testOfWithDuplicateKey() { 891 try { 892 ImmutableMap.of("one", 1, "one", 1); 893 fail(); 894 } catch (IllegalArgumentException expected) { 895 } 896 } 897 testCopyOfEmptyMap()898 public void testCopyOfEmptyMap() { 899 ImmutableMap<String, Integer> copy = 900 ImmutableMap.copyOf(Collections.<String, Integer>emptyMap()); 901 assertEquals(Collections.<String, Integer>emptyMap(), copy); 902 assertSame(copy, ImmutableMap.copyOf(copy)); 903 } 904 testCopyOfSingletonMap()905 public void testCopyOfSingletonMap() { 906 ImmutableMap<String, Integer> copy = ImmutableMap.copyOf(Collections.singletonMap("one", 1)); 907 assertMapEquals(copy, "one", 1); 908 assertSame(copy, ImmutableMap.copyOf(copy)); 909 } 910 testCopyOf()911 public void testCopyOf() { 912 Map<String, Integer> original = new LinkedHashMap<>(); 913 original.put("one", 1); 914 original.put("two", 2); 915 original.put("three", 3); 916 917 ImmutableMap<String, Integer> copy = ImmutableMap.copyOf(original); 918 assertMapEquals(copy, "one", 1, "two", 2, "three", 3); 919 assertSame(copy, ImmutableMap.copyOf(copy)); 920 } 921 922 // TODO(b/172823566): Use mainline testToImmutableMap once CollectorTester is usable to java7. testToImmutableMap_java7_combine()923 public void testToImmutableMap_java7_combine() { 924 ImmutableMap.Builder<String, Integer> zis = 925 ImmutableMap.<String, Integer>builder().put("one", 1); 926 ImmutableMap.Builder<String, Integer> zat = 927 ImmutableMap.<String, Integer>builder().put("two", 2).put("three", 3); 928 assertMapEquals(zis.combine(zat).build(), "one", 1, "two", 2, "three", 3); 929 } 930 931 // TODO(b/172823566): Use mainline testToImmutableMap once CollectorTester is usable to java7. testToImmutableMap_exceptionOnDuplicateKey_java7_combine()932 public void testToImmutableMap_exceptionOnDuplicateKey_java7_combine() { 933 ImmutableMap.Builder<String, Integer> zis = 934 ImmutableMap.<String, Integer>builder().put("one", 1).put("two", 2); 935 ImmutableMap.Builder<String, Integer> zat = 936 ImmutableMap.<String, Integer>builder().put("two", 22).put("three", 3); 937 try { 938 zis.combine(zat).build(); 939 fail("Expected IllegalArgumentException"); 940 } catch (IllegalArgumentException expected) { 941 // expected 942 } 943 } 944 hashtableTestHelper(ImmutableList<Integer> sizes)945 public static void hashtableTestHelper(ImmutableList<Integer> sizes) { 946 for (int size : sizes) { 947 Builder<Integer, Integer> builder = ImmutableMap.builderWithExpectedSize(size); 948 for (int i = 0; i < size; i++) { 949 Integer integer = i; 950 builder.put(integer, integer); 951 } 952 ImmutableMap<Integer, Integer> map = builder.build(); 953 assertEquals(size, map.size()); 954 int entries = 0; 955 for (Integer key : map.keySet()) { 956 assertEquals(entries, key.intValue()); 957 assertSame(key, map.get(key)); 958 entries++; 959 } 960 assertEquals(size, entries); 961 } 962 } 963 testByteArrayHashtable()964 public void testByteArrayHashtable() { 965 hashtableTestHelper(ImmutableList.of(2, 89)); 966 } 967 testShortArrayHashtable()968 public void testShortArrayHashtable() { 969 hashtableTestHelper(ImmutableList.of(90, 22937)); 970 } 971 testIntArrayHashtable()972 public void testIntArrayHashtable() { 973 hashtableTestHelper(ImmutableList.of(22938)); 974 } 975 } 976 testNullGet()977 public void testNullGet() { 978 ImmutableMap<String, Integer> map = ImmutableMap.of("one", 1); 979 assertNull(map.get(null)); 980 } 981 testAsMultimap()982 public void testAsMultimap() { 983 ImmutableMap<String, Integer> map = 984 ImmutableMap.of("one", 1, "won", 1, "two", 2, "too", 2, "three", 3); 985 ImmutableSetMultimap<String, Integer> expected = 986 ImmutableSetMultimap.of("one", 1, "won", 1, "two", 2, "too", 2, "three", 3); 987 assertEquals(expected, map.asMultimap()); 988 } 989 testAsMultimapWhenEmpty()990 public void testAsMultimapWhenEmpty() { 991 ImmutableMap<String, Integer> map = ImmutableMap.of(); 992 ImmutableSetMultimap<String, Integer> expected = ImmutableSetMultimap.of(); 993 assertEquals(expected, map.asMultimap()); 994 } 995 testAsMultimapCaches()996 public void testAsMultimapCaches() { 997 ImmutableMap<String, Integer> map = ImmutableMap.of("one", 1); 998 ImmutableSetMultimap<String, Integer> multimap1 = map.asMultimap(); 999 ImmutableSetMultimap<String, Integer> multimap2 = map.asMultimap(); 1000 assertEquals(1, multimap1.asMap().size()); 1001 assertSame(multimap1, multimap2); 1002 } 1003 1004 @GwtIncompatible // NullPointerTester testNullPointers()1005 public void testNullPointers() { 1006 NullPointerTester tester = new NullPointerTester(); 1007 tester.testAllPublicStaticMethods(ImmutableMap.class); 1008 tester.testAllPublicInstanceMethods(new ImmutableMap.Builder<Object, Object>()); 1009 tester.testAllPublicInstanceMethods(ImmutableMap.of()); 1010 tester.testAllPublicInstanceMethods(ImmutableMap.of("one", 1)); 1011 tester.testAllPublicInstanceMethods(ImmutableMap.of("one", 1, "two", 2, "three", 3)); 1012 } 1013 assertMapEquals(Map<K, V> map, Object... alternatingKeysAndValues)1014 private static <K, V> void assertMapEquals(Map<K, V> map, Object... alternatingKeysAndValues) { 1015 Map<Object, Object> expected = new LinkedHashMap<>(); 1016 for (int i = 0; i < alternatingKeysAndValues.length; i += 2) { 1017 expected.put(alternatingKeysAndValues[i], alternatingKeysAndValues[i + 1]); 1018 } 1019 assertThat(map).containsExactlyEntriesIn(expected).inOrder(); 1020 } 1021 1022 private static class IntHolder implements Serializable { 1023 public int value; 1024 IntHolder(int value)1025 public IntHolder(int value) { 1026 this.value = value; 1027 } 1028 1029 @Override equals(Object o)1030 public boolean equals(Object o) { 1031 return (o instanceof IntHolder) && ((IntHolder) o).value == value; 1032 } 1033 1034 @Override hashCode()1035 public int hashCode() { 1036 return value; 1037 } 1038 1039 private static final long serialVersionUID = 5; 1040 } 1041 testMutableValues()1042 public void testMutableValues() { 1043 IntHolder holderA = new IntHolder(1); 1044 IntHolder holderB = new IntHolder(2); 1045 Map<String, IntHolder> map = ImmutableMap.of("a", holderA, "b", holderB); 1046 holderA.value = 3; 1047 assertTrue(map.entrySet().contains(Maps.immutableEntry("a", new IntHolder(3)))); 1048 Map<String, Integer> intMap = ImmutableMap.of("a", 3, "b", 2); 1049 assertEquals(intMap.hashCode(), map.entrySet().hashCode()); 1050 assertEquals(intMap.hashCode(), map.hashCode()); 1051 } 1052 1053 @GwtIncompatible // SerializableTester testViewSerialization()1054 public void testViewSerialization() { 1055 Map<String, Integer> map = ImmutableMap.of("one", 1, "two", 2, "three", 3); 1056 LenientSerializableTester.reserializeAndAssertLenient(map.entrySet()); 1057 LenientSerializableTester.reserializeAndAssertLenient(map.keySet()); 1058 1059 Collection<Integer> reserializedValues = reserialize(map.values()); 1060 assertEquals(Lists.newArrayList(map.values()), Lists.newArrayList(reserializedValues)); 1061 assertTrue(reserializedValues instanceof ImmutableCollection); 1062 } 1063 1064 @GwtIncompatible // SerializableTester testKeySetIsSerializable_regularImmutableMap()1065 public void testKeySetIsSerializable_regularImmutableMap() { 1066 class NonSerializableClass {} 1067 1068 Map<String, NonSerializableClass> map = 1069 RegularImmutableMap.create(1, new Object[] {"one", new NonSerializableClass()}); 1070 Set<String> set = map.keySet(); 1071 1072 LenientSerializableTester.reserializeAndAssertLenient(set); 1073 } 1074 1075 @GwtIncompatible // SerializableTester testValuesCollectionIsSerializable_regularImmutableMap()1076 public void testValuesCollectionIsSerializable_regularImmutableMap() { 1077 class NonSerializableClass {} 1078 1079 Map<NonSerializableClass, String> map = 1080 RegularImmutableMap.create(1, new Object[] {new NonSerializableClass(), "value"}); 1081 Collection<String> collection = map.values(); 1082 1083 LenientSerializableTester.reserializeAndAssertElementsEqual(collection); 1084 } 1085 1086 // TODO: Re-enable this test after moving to new serialization format in ImmutableMap. 1087 @GwtIncompatible // SerializableTester 1088 @SuppressWarnings("unchecked") ignore_testSerializationNoDuplication_regularImmutableMap()1089 public void ignore_testSerializationNoDuplication_regularImmutableMap() throws Exception { 1090 // Tests that searializing a map, its keySet, and values only writes the underlying data once. 1091 1092 Object[] entries = new Object[2000]; 1093 for (int i = 0; i < entries.length; i++) { 1094 entries[i] = i; 1095 } 1096 1097 ImmutableMap<Integer, Integer> map = RegularImmutableMap.create(entries.length / 2, entries); 1098 Set<Integer> keySet = map.keySet(); 1099 Collection<Integer> values = map.values(); 1100 1101 ByteArrayOutputStream bytes = new ByteArrayOutputStream(); 1102 ObjectOutputStream oos = new ObjectOutputStream(bytes); 1103 oos.writeObject(map); 1104 oos.flush(); 1105 1106 int mapSize = bytes.size(); 1107 oos.writeObject(keySet); 1108 oos.writeObject(values); 1109 oos.close(); 1110 1111 int finalSize = bytes.size(); 1112 1113 assertThat(finalSize - mapSize).isLessThan(100); 1114 } 1115 testEquals()1116 public void testEquals() { 1117 new EqualsTester() 1118 .addEqualityGroup( 1119 ImmutableMap.of(), 1120 ImmutableMap.builder().buildOrThrow(), 1121 ImmutableMap.ofEntries(), 1122 map()) 1123 .addEqualityGroup( 1124 ImmutableMap.of(1, 1), 1125 ImmutableMap.builder().put(1, 1).buildOrThrow(), 1126 ImmutableMap.ofEntries(entry(1, 1)), 1127 map(1, 1)) 1128 .addEqualityGroup( 1129 ImmutableMap.of(1, 1, 2, 2), 1130 ImmutableMap.builder().put(1, 1).put(2, 2).buildOrThrow(), 1131 ImmutableMap.ofEntries(entry(1, 1), entry(2, 2)), 1132 map(1, 1, 2, 2)) 1133 .addEqualityGroup( 1134 ImmutableMap.of(1, 1, 2, 2, 3, 3), 1135 ImmutableMap.builder().put(1, 1).put(2, 2).put(3, 3).buildOrThrow(), 1136 ImmutableMap.ofEntries(entry(1, 1), entry(2, 2), entry(3, 3)), 1137 map(1, 1, 2, 2, 3, 3)) 1138 .addEqualityGroup( 1139 ImmutableMap.of(1, 4, 2, 2, 3, 3), 1140 ImmutableMap.builder().put(1, 4).put(2, 2).put(3, 3).buildOrThrow(), 1141 ImmutableMap.ofEntries(entry(1, 4), entry(2, 2), entry(3, 3)), 1142 map(1, 4, 2, 2, 3, 3)) 1143 .addEqualityGroup( 1144 ImmutableMap.of(1, 1, 2, 4, 3, 3), 1145 ImmutableMap.builder().put(1, 1).put(2, 4).put(3, 3).buildOrThrow(), 1146 ImmutableMap.ofEntries(entry(1, 1), entry(2, 4), entry(3, 3)), 1147 map(1, 1, 2, 4, 3, 3)) 1148 .addEqualityGroup( 1149 ImmutableMap.of(1, 1, 2, 2, 3, 4), 1150 ImmutableMap.builder().put(1, 1).put(2, 2).put(3, 4).buildOrThrow(), 1151 ImmutableMap.ofEntries(entry(1, 1), entry(2, 2), entry(3, 4)), 1152 map(1, 1, 2, 2, 3, 4)) 1153 .addEqualityGroup( 1154 ImmutableMap.of(1, 2, 2, 3, 3, 1), 1155 ImmutableMap.builder().put(1, 2).put(2, 3).put(3, 1).buildOrThrow(), 1156 ImmutableMap.ofEntries(entry(1, 2), entry(2, 3), entry(3, 1)), 1157 map(1, 2, 2, 3, 3, 1)) 1158 .addEqualityGroup( 1159 ImmutableMap.of(1, 1, 2, 2, 3, 3, 4, 4), 1160 ImmutableMap.builder().put(1, 1).put(2, 2).put(3, 3).put(4, 4).buildOrThrow(), 1161 ImmutableMap.ofEntries(entry(1, 1), entry(2, 2), entry(3, 3), entry(4, 4)), 1162 map(1, 1, 2, 2, 3, 3, 4, 4)) 1163 .addEqualityGroup( 1164 ImmutableMap.of(1, 1, 2, 2, 3, 3, 4, 4, 5, 5), 1165 ImmutableMap.builder().put(1, 1).put(2, 2).put(3, 3).put(4, 4).put(5, 5).buildOrThrow(), 1166 ImmutableMap.ofEntries(entry(1, 1), entry(2, 2), entry(3, 3), entry(4, 4), entry(5, 5)), 1167 map(1, 1, 2, 2, 3, 3, 4, 4, 5, 5)) 1168 .testEquals(); 1169 } 1170 testOfEntriesNull()1171 public void testOfEntriesNull() { 1172 Entry<Integer, Integer> nullKey = entry(null, 23); 1173 try { 1174 ImmutableMap.ofEntries(nullKey); 1175 fail(); 1176 } catch (NullPointerException expected) { 1177 } 1178 Entry<Integer, Integer> nullValue = entry(23, null); 1179 try { 1180 ImmutableMap.ofEntries(nullValue); 1181 fail(); 1182 } catch (NullPointerException expected) { 1183 } 1184 } 1185 map(T... keysAndValues)1186 private static <T> Map<T, T> map(T... keysAndValues) { 1187 assertThat(keysAndValues.length % 2).isEqualTo(0); 1188 LinkedHashMap<T, T> map = new LinkedHashMap<>(); 1189 for (int i = 0; i < keysAndValues.length; i += 2) { 1190 T key = keysAndValues[i]; 1191 T value = keysAndValues[i + 1]; 1192 T old = map.put(key, value); 1193 assertWithMessage("Key %s set to %s and %s", key, value, old).that(old).isNull(); 1194 } 1195 return map; 1196 } 1197 entry(T key, T value)1198 private static <T> Entry<T, T> entry(T key, T value) { 1199 return new AbstractMap.SimpleImmutableEntry<>(key, value); 1200 } 1201 } 1202