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 17package com.google.common.collect; 18 19import static com.google.common.testing.SerializableTester.reserialize; 20 21import com.google.common.annotations.GwtCompatible; 22import com.google.common.annotations.GwtIncompatible; 23import com.google.common.base.Joiner; 24import com.google.common.collect.ImmutableMap.Builder; 25import com.google.common.collect.testing.CollectionTestSuiteBuilder; 26import com.google.common.collect.testing.ListTestSuiteBuilder; 27import com.google.common.collect.testing.MapInterfaceTest; 28import com.google.common.collect.testing.MinimalSet; 29import com.google.common.collect.testing.ReserializingTestCollectionGenerator; 30import com.google.common.collect.testing.ReserializingTestSetGenerator; 31import com.google.common.collect.testing.SampleElements.Colliders; 32import com.google.common.collect.testing.SampleElements.Unhashables; 33import com.google.common.collect.testing.SetTestSuiteBuilder; 34import com.google.common.collect.testing.UnhashableObject; 35import com.google.common.collect.testing.features.CollectionFeature; 36import com.google.common.collect.testing.features.CollectionSize; 37import com.google.common.collect.testing.google.MapGenerators.ImmutableMapEntrySetGenerator; 38import com.google.common.collect.testing.google.MapGenerators.ImmutableMapKeySetGenerator; 39import com.google.common.collect.testing.google.MapGenerators.ImmutableMapUnhashableValuesGenerator; 40import com.google.common.collect.testing.google.MapGenerators.ImmutableMapValueListGenerator; 41import com.google.common.collect.testing.google.MapGenerators.ImmutableMapValuesGenerator; 42import com.google.common.testing.NullPointerTester; 43import com.google.common.testing.SerializableTester; 44 45import junit.framework.Test; 46import junit.framework.TestCase; 47import junit.framework.TestSuite; 48 49import java.io.Serializable; 50import java.util.Collection; 51import java.util.Collections; 52import java.util.LinkedHashMap; 53import java.util.Map; 54import java.util.Map.Entry; 55 56/** 57 * Tests for {@link ImmutableMap}. 58 * 59 * @author Kevin Bourrillion 60 * @author Jesse Wilson 61 */ 62@GwtCompatible(emulated = true) 63public class ImmutableMapTest extends TestCase { 64 65 @GwtIncompatible("suite") 66 public static Test suite() { 67 TestSuite suite = new TestSuite(); 68 suite.addTestSuite(ImmutableMapTest.class); 69 70 suite.addTest(SetTestSuiteBuilder.using(new ImmutableMapKeySetGenerator()) 71 .withFeatures( 72 CollectionSize.ANY, 73 CollectionFeature.KNOWN_ORDER, 74 CollectionFeature.REJECTS_DUPLICATES_AT_CREATION, 75 CollectionFeature.ALLOWS_NULL_QUERIES) 76 .named("ImmutableMap.keySet") 77 .createTestSuite()); 78 79 suite.addTest(SetTestSuiteBuilder.using(new ImmutableMapEntrySetGenerator()) 80 .withFeatures( 81 CollectionSize.ANY, 82 CollectionFeature.KNOWN_ORDER, 83 CollectionFeature.REJECTS_DUPLICATES_AT_CREATION, 84 CollectionFeature.ALLOWS_NULL_QUERIES) 85 .named("ImmutableMap.entrySet") 86 .createTestSuite()); 87 88 suite.addTest(CollectionTestSuiteBuilder.using( 89 new ImmutableMapValuesGenerator()) 90 .withFeatures(CollectionSize.ANY, CollectionFeature.KNOWN_ORDER, 91 CollectionFeature.ALLOWS_NULL_QUERIES) 92 .named("ImmutableMap.values") 93 .createTestSuite()); 94 95 suite.addTest(SetTestSuiteBuilder.using( 96 ReserializingTestSetGenerator.newInstance( 97 new ImmutableMapKeySetGenerator())) 98 .withFeatures( 99 CollectionSize.ANY, 100 CollectionFeature.KNOWN_ORDER, 101 CollectionFeature.REJECTS_DUPLICATES_AT_CREATION, 102 CollectionFeature.ALLOWS_NULL_QUERIES) 103 .named("ImmutableMap.keySet, reserialized") 104 .createTestSuite()); 105 106 suite.addTest(SetTestSuiteBuilder.using( 107 ReserializingTestSetGenerator.newInstance( 108 new ImmutableMapKeySetGenerator())) 109 .withFeatures( 110 CollectionSize.ANY, 111 CollectionFeature.KNOWN_ORDER, 112 CollectionFeature.REJECTS_DUPLICATES_AT_CREATION, 113 CollectionFeature.ALLOWS_NULL_QUERIES) 114 .named("ImmutableMap.entrySet, reserialized") 115 .createTestSuite()); 116 117 suite.addTest(CollectionTestSuiteBuilder.using( 118 ReserializingTestCollectionGenerator.newInstance( 119 new ImmutableMapValuesGenerator())) 120 .withFeatures(CollectionSize.ANY, CollectionFeature.KNOWN_ORDER, 121 CollectionFeature.ALLOWS_NULL_QUERIES) 122 .named("ImmutableMap.values, reserialized") 123 .createTestSuite()); 124 125 suite.addTest(CollectionTestSuiteBuilder.using( 126 new ImmutableMapUnhashableValuesGenerator()) 127 .withFeatures(CollectionSize.ANY, CollectionFeature.KNOWN_ORDER, 128 CollectionFeature.ALLOWS_NULL_QUERIES) 129 .named("ImmutableMap.values, unhashable") 130 .createTestSuite()); 131 132 suite.addTest(ListTestSuiteBuilder.using( 133 new ImmutableMapValueListGenerator()) 134 .named("ImmutableMap.values.asList") 135 .withFeatures(CollectionSize.ANY, 136 CollectionFeature.ALLOWS_NULL_QUERIES) 137 .createTestSuite()); 138 139 return suite; 140 } 141 142 public abstract static class AbstractMapTests<K, V> 143 extends MapInterfaceTest<K, V> { 144 public AbstractMapTests() { 145 super(false, false, false, false, false); 146 } 147 148 @Override protected Map<K, V> makeEmptyMap() { 149 throw new UnsupportedOperationException(); 150 } 151 152 private static final Joiner joiner = Joiner.on(", "); 153 154 @Override protected void assertMoreInvariants(Map<K, V> map) { 155 // TODO: can these be moved to MapInterfaceTest? 156 for (Entry<K, V> entry : map.entrySet()) { 157 assertEquals(entry.getKey() + "=" + entry.getValue(), 158 entry.toString()); 159 } 160 161 assertEquals("{" + joiner.join(map.entrySet()) + "}", 162 map.toString()); 163 assertEquals("[" + joiner.join(map.entrySet()) + "]", 164 map.entrySet().toString()); 165 assertEquals("[" + joiner.join(map.keySet()) + "]", 166 map.keySet().toString()); 167 assertEquals("[" + joiner.join(map.values()) + "]", 168 map.values().toString()); 169 170 assertEquals(MinimalSet.from(map.entrySet()), map.entrySet()); 171 assertEquals(Sets.newHashSet(map.keySet()), map.keySet()); 172 } 173 } 174 175 public static class MapTests extends AbstractMapTests<String, Integer> { 176 @Override protected Map<String, Integer> makeEmptyMap() { 177 return ImmutableMap.of(); 178 } 179 180 @Override protected Map<String, Integer> makePopulatedMap() { 181 return ImmutableMap.of("one", 1, "two", 2, "three", 3); 182 } 183 184 @Override protected String getKeyNotInPopulatedMap() { 185 return "minus one"; 186 } 187 188 @Override protected Integer getValueNotInPopulatedMap() { 189 return -1; 190 } 191 } 192 193 public static class SingletonMapTests 194 extends AbstractMapTests<String, Integer> { 195 @Override protected Map<String, Integer> makePopulatedMap() { 196 return ImmutableMap.of("one", 1); 197 } 198 199 @Override protected String getKeyNotInPopulatedMap() { 200 return "minus one"; 201 } 202 203 @Override protected Integer getValueNotInPopulatedMap() { 204 return -1; 205 } 206 } 207 208 @GwtIncompatible("SerializableTester") 209 public static class ReserializedMapTests 210 extends AbstractMapTests<String, Integer> { 211 @Override protected Map<String, Integer> makePopulatedMap() { 212 return SerializableTester.reserialize( 213 ImmutableMap.of("one", 1, "two", 2, "three", 3)); 214 } 215 216 @Override protected String getKeyNotInPopulatedMap() { 217 return "minus one"; 218 } 219 220 @Override protected Integer getValueNotInPopulatedMap() { 221 return -1; 222 } 223 } 224 225 public static class MapTestsWithBadHashes 226 extends AbstractMapTests<Object, Integer> { 227 228 @Override protected Map<Object, Integer> makeEmptyMap() { 229 throw new UnsupportedOperationException(); 230 } 231 232 @Override protected Map<Object, Integer> makePopulatedMap() { 233 Colliders colliders = new Colliders(); 234 return ImmutableMap.of( 235 colliders.e0, 0, 236 colliders.e1, 1, 237 colliders.e2, 2, 238 colliders.e3, 3); 239 } 240 241 @Override protected Object getKeyNotInPopulatedMap() { 242 return new Colliders().e4; 243 } 244 245 @Override protected Integer getValueNotInPopulatedMap() { 246 return 4; 247 } 248 } 249 250 @GwtIncompatible("GWT's ImmutableMap emulation is backed by java.util.HashMap.") 251 public static class MapTestsWithUnhashableValues 252 extends AbstractMapTests<Integer, UnhashableObject> { 253 @Override protected Map<Integer, UnhashableObject> makeEmptyMap() { 254 return ImmutableMap.of(); 255 } 256 257 @Override protected Map<Integer, UnhashableObject> makePopulatedMap() { 258 Unhashables unhashables = new Unhashables(); 259 return ImmutableMap.of( 260 0, unhashables.e0, 1, unhashables.e1, 2, unhashables.e2); 261 } 262 263 @Override protected Integer getKeyNotInPopulatedMap() { 264 return 3; 265 } 266 267 @Override protected UnhashableObject getValueNotInPopulatedMap() { 268 return new Unhashables().e3; 269 } 270 } 271 272 @GwtIncompatible("GWT's ImmutableMap emulation is backed by java.util.HashMap.") 273 public static class MapTestsWithSingletonUnhashableValue 274 extends MapTestsWithUnhashableValues { 275 @Override protected Map<Integer, UnhashableObject> makePopulatedMap() { 276 Unhashables unhashables = new Unhashables(); 277 return ImmutableMap.of(0, unhashables.e0); 278 } 279 } 280 281 public static class CreationTests extends TestCase { 282 public void testEmptyBuilder() { 283 ImmutableMap<String, Integer> map 284 = new Builder<String, Integer>().build(); 285 assertEquals(Collections.<String, Integer>emptyMap(), map); 286 } 287 288 public void testSingletonBuilder() { 289 ImmutableMap<String, Integer> map = new Builder<String, Integer>() 290 .put("one", 1) 291 .build(); 292 assertMapEquals(map, "one", 1); 293 } 294 295 public void testBuilder() { 296 ImmutableMap<String, Integer> map = new Builder<String, Integer>() 297 .put("one", 1) 298 .put("two", 2) 299 .put("three", 3) 300 .put("four", 4) 301 .put("five", 5) 302 .build(); 303 assertMapEquals(map, 304 "one", 1, "two", 2, "three", 3, "four", 4, "five", 5); 305 } 306 307 public void testBuilder_withImmutableEntry() { 308 ImmutableMap<String, Integer> map = new Builder<String, Integer>() 309 .put(Maps.immutableEntry("one", 1)) 310 .build(); 311 assertMapEquals(map, "one", 1); 312 } 313 314 public void testBuilder_withImmutableEntryAndNullContents() { 315 Builder<String, Integer> builder = new Builder<String, Integer>(); 316 try { 317 builder.put(Maps.immutableEntry("one", (Integer) null)); 318 fail(); 319 } catch (NullPointerException expected) { 320 } 321 try { 322 builder.put(Maps.immutableEntry((String) null, 1)); 323 fail(); 324 } catch (NullPointerException expected) { 325 } 326 } 327 328 private static class StringHolder { 329 String string; 330 } 331 332 public void testBuilder_withMutableEntry() { 333 ImmutableMap.Builder<String, Integer> builder = 334 new Builder<String, Integer>(); 335 final StringHolder holder = new StringHolder(); 336 holder.string = "one"; 337 Entry<String, Integer> entry = new AbstractMapEntry<String, Integer>() { 338 @Override public String getKey() { 339 return holder.string; 340 } 341 @Override public Integer getValue() { 342 return 1; 343 } 344 }; 345 346 builder.put(entry); 347 holder.string = "two"; 348 assertMapEquals(builder.build(), "one", 1); 349 } 350 351 public void testBuilderPutAllWithEmptyMap() { 352 ImmutableMap<String, Integer> map = new Builder<String, Integer>() 353 .putAll(Collections.<String, Integer>emptyMap()) 354 .build(); 355 assertEquals(Collections.<String, Integer>emptyMap(), map); 356 } 357 358 public void testBuilderPutAll() { 359 Map<String, Integer> toPut = new LinkedHashMap<String, Integer>(); 360 toPut.put("one", 1); 361 toPut.put("two", 2); 362 toPut.put("three", 3); 363 Map<String, Integer> moreToPut = new LinkedHashMap<String, Integer>(); 364 moreToPut.put("four", 4); 365 moreToPut.put("five", 5); 366 367 ImmutableMap<String, Integer> map = new Builder<String, Integer>() 368 .putAll(toPut) 369 .putAll(moreToPut) 370 .build(); 371 assertMapEquals(map, 372 "one", 1, "two", 2, "three", 3, "four", 4, "five", 5); 373 } 374 375 public void testBuilderReuse() { 376 Builder<String, Integer> builder = new Builder<String, Integer>(); 377 ImmutableMap<String, Integer> mapOne = builder 378 .put("one", 1) 379 .put("two", 2) 380 .build(); 381 ImmutableMap<String, Integer> mapTwo = builder 382 .put("three", 3) 383 .put("four", 4) 384 .build(); 385 386 assertMapEquals(mapOne, "one", 1, "two", 2); 387 assertMapEquals(mapTwo, "one", 1, "two", 2, "three", 3, "four", 4); 388 } 389 390 public void testBuilderPutNullKey() { 391 Builder<String, Integer> builder = new Builder<String, Integer>(); 392 try { 393 builder.put(null, 1); 394 fail(); 395 } catch (NullPointerException expected) { 396 } 397 } 398 399 public void testBuilderPutNullValue() { 400 Builder<String, Integer> builder = new Builder<String, Integer>(); 401 try { 402 builder.put("one", null); 403 fail(); 404 } catch (NullPointerException expected) { 405 } 406 } 407 408 public void testBuilderPutNullKeyViaPutAll() { 409 Builder<String, Integer> builder = new Builder<String, Integer>(); 410 try { 411 builder.putAll(Collections.<String, Integer>singletonMap(null, 1)); 412 fail(); 413 } catch (NullPointerException expected) { 414 } 415 } 416 417 public void testBuilderPutNullValueViaPutAll() { 418 Builder<String, Integer> builder = new Builder<String, Integer>(); 419 try { 420 builder.putAll(Collections.<String, Integer>singletonMap("one", null)); 421 fail(); 422 } catch (NullPointerException expected) { 423 } 424 } 425 426 public void testPuttingTheSameKeyTwiceThrowsOnBuild() { 427 Builder<String, Integer> builder = new Builder<String, Integer>() 428 .put("one", 1) 429 .put("one", 1); // throwing on this line would be even better 430 431 try { 432 builder.build(); 433 fail(); 434 } catch (IllegalArgumentException expected) { 435 assertEquals("duplicate key: one", expected.getMessage()); 436 } 437 } 438 439 public void testOf() { 440 assertMapEquals( 441 ImmutableMap.of("one", 1), 442 "one", 1); 443 assertMapEquals( 444 ImmutableMap.of("one", 1, "two", 2), 445 "one", 1, "two", 2); 446 assertMapEquals( 447 ImmutableMap.of("one", 1, "two", 2, "three", 3), 448 "one", 1, "two", 2, "three", 3); 449 assertMapEquals( 450 ImmutableMap.of("one", 1, "two", 2, "three", 3, "four", 4), 451 "one", 1, "two", 2, "three", 3, "four", 4); 452 assertMapEquals( 453 ImmutableMap.of("one", 1, "two", 2, "three", 3, "four", 4, "five", 5), 454 "one", 1, "two", 2, "three", 3, "four", 4, "five", 5); 455 } 456 457 public void testOfNullKey() { 458 try { 459 ImmutableMap.of(null, 1); 460 fail(); 461 } catch (NullPointerException expected) { 462 } 463 464 try { 465 ImmutableMap.of("one", 1, null, 2); 466 fail(); 467 } catch (NullPointerException expected) { 468 } 469 } 470 471 public void testOfNullValue() { 472 try { 473 ImmutableMap.of("one", null); 474 fail(); 475 } catch (NullPointerException expected) { 476 } 477 478 try { 479 ImmutableMap.of("one", 1, "two", null); 480 fail(); 481 } catch (NullPointerException expected) { 482 } 483 } 484 485 public void testOfWithDuplicateKey() { 486 try { 487 ImmutableMap.of("one", 1, "one", 1); 488 fail(); 489 } catch (IllegalArgumentException expected) { 490 assertEquals("duplicate key: one", expected.getMessage()); 491 } 492 } 493 494 public void testCopyOfEmptyMap() { 495 ImmutableMap<String, Integer> copy 496 = ImmutableMap.copyOf(Collections.<String, Integer>emptyMap()); 497 assertEquals(Collections.<String, Integer>emptyMap(), copy); 498 assertSame(copy, ImmutableMap.copyOf(copy)); 499 } 500 501 public void testCopyOfSingletonMap() { 502 ImmutableMap<String, Integer> copy 503 = ImmutableMap.copyOf(Collections.singletonMap("one", 1)); 504 assertMapEquals(copy, "one", 1); 505 assertSame(copy, ImmutableMap.copyOf(copy)); 506 } 507 508 public void testCopyOf() { 509 Map<String, Integer> original = new LinkedHashMap<String, Integer>(); 510 original.put("one", 1); 511 original.put("two", 2); 512 original.put("three", 3); 513 514 ImmutableMap<String, Integer> copy = ImmutableMap.copyOf(original); 515 assertMapEquals(copy, "one", 1, "two", 2, "three", 3); 516 assertSame(copy, ImmutableMap.copyOf(copy)); 517 } 518 } 519 520 public void testNullGet() { 521 ImmutableMap<String, Integer> map = ImmutableMap.of("one", 1); 522 assertNull(map.get(null)); 523 } 524 525 @GwtIncompatible("NullPointerTester") 526 public void testNullPointers() throws Exception { 527 NullPointerTester tester = new NullPointerTester(); 528 tester.testAllPublicStaticMethods(ImmutableMap.class); 529 tester.testAllPublicInstanceMethods( 530 new ImmutableMap.Builder<Object, Object>()); 531 tester.testAllPublicInstanceMethods(ImmutableMap.of()); 532 tester.testAllPublicInstanceMethods(ImmutableMap.of("one", 1)); 533 tester.testAllPublicInstanceMethods( 534 ImmutableMap.of("one", 1, "two", 2, "three", 3)); 535 } 536 537 private static <K, V> void assertMapEquals(Map<K, V> map, 538 Object... alternatingKeysAndValues) { 539 assertEquals(map.size(), alternatingKeysAndValues.length / 2); 540 int i = 0; 541 for (Entry<K, V> entry : map.entrySet()) { 542 assertEquals(alternatingKeysAndValues[i++], entry.getKey()); 543 assertEquals(alternatingKeysAndValues[i++], entry.getValue()); 544 } 545 } 546 547 private static class IntHolder implements Serializable { 548 public int value; 549 550 public IntHolder(int value) { 551 this.value = value; 552 } 553 554 @Override public boolean equals(Object o) { 555 return (o instanceof IntHolder) && ((IntHolder) o).value == value; 556 } 557 558 @Override public int hashCode() { 559 return value; 560 } 561 562 private static final long serialVersionUID = 5; 563 } 564 565 public void testMutableValues() { 566 IntHolder holderA = new IntHolder(1); 567 IntHolder holderB = new IntHolder(2); 568 Map<String, IntHolder> map = ImmutableMap.of("a", holderA, "b", holderB); 569 holderA.value = 3; 570 assertTrue(map.entrySet().contains( 571 Maps.immutableEntry("a", new IntHolder(3)))); 572 Map<String, Integer> intMap = ImmutableMap.of("a", 3, "b", 2); 573 assertEquals(intMap.hashCode(), map.entrySet().hashCode()); 574 assertEquals(intMap.hashCode(), map.hashCode()); 575 } 576 577 @GwtIncompatible("SerializableTester") 578 public void testViewSerialization() { 579 Map<String, Integer> map = ImmutableMap.of("one", 1, "two", 2, "three", 3); 580 LenientSerializableTester.reserializeAndAssertLenient(map.entrySet()); 581 LenientSerializableTester.reserializeAndAssertLenient(map.keySet()); 582 583 Collection<Integer> reserializedValues = reserialize(map.values()); 584 assertEquals(Lists.newArrayList(map.values()), 585 Lists.newArrayList(reserializedValues)); 586 assertTrue(reserializedValues instanceof ImmutableCollection); 587 } 588} 589