001/* 002 * Copyright (C) 2009 The Guava Authors 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package com.google.common.collect.testing; 018 019import static java.util.Arrays.asList; 020import static java.util.Collections.emptyMap; 021import static java.util.Collections.emptySet; 022import static java.util.Collections.singletonMap; 023import static java.util.Collections.unmodifiableMap; 024 025import com.google.common.annotations.GwtIncompatible; 026import com.google.common.collect.testing.features.CollectionFeature; 027import com.google.common.collect.testing.features.CollectionSize; 028import com.google.common.collect.testing.features.MapFeature; 029import com.google.common.collect.testing.testers.MapEntrySetTester; 030import com.google.errorprone.annotations.CanIgnoreReturnValue; 031import java.io.Serializable; 032import java.lang.reflect.Method; 033import java.util.Collection; 034import java.util.Collections; 035import java.util.Comparator; 036import java.util.EnumMap; 037import java.util.HashMap; 038import java.util.Hashtable; 039import java.util.LinkedHashMap; 040import java.util.Map; 041import java.util.Map.Entry; 042import java.util.NavigableMap; 043import java.util.SortedMap; 044import java.util.TreeMap; 045import java.util.concurrent.ConcurrentHashMap; 046import java.util.concurrent.ConcurrentSkipListMap; 047import junit.framework.Test; 048import junit.framework.TestSuite; 049 050/** 051 * Generates a test suite covering the {@link Map} implementations in the {@link java.util} package. 052 * Can be subclassed to specify tests that should be suppressed. 053 * 054 * @author Kevin Bourrillion 055 */ 056@GwtIncompatible 057public class TestsForMapsInJavaUtil { 058 059 public static Test suite() { 060 return new TestsForMapsInJavaUtil().allTests(); 061 } 062 063 public Test allTests() { 064 TestSuite suite = new TestSuite("java.util Maps"); 065 suite.addTest(testsForCheckedMap()); 066 suite.addTest(testsForCheckedNavigableMap()); 067 suite.addTest(testsForCheckedSortedMap()); 068 suite.addTest(testsForEmptyMap()); 069 suite.addTest(testsForEmptyNavigableMap()); 070 suite.addTest(testsForEmptySortedMap()); 071 suite.addTest(testsForSingletonMap()); 072 suite.addTest(testsForHashMap()); 073 suite.addTest(testsForHashtable()); 074 suite.addTest(testsForLinkedHashMap()); 075 suite.addTest(testsForSynchronizedNavigableMap()); 076 suite.addTest(testsForTreeMapNatural()); 077 suite.addTest(testsForTreeMapWithComparator()); 078 suite.addTest(testsForUnmodifiableMap()); 079 suite.addTest(testsForUnmodifiableNavigableMap()); 080 suite.addTest(testsForUnmodifiableSortedMap()); 081 suite.addTest(testsForEnumMap()); 082 suite.addTest(testsForConcurrentHashMap()); 083 suite.addTest(testsForConcurrentSkipListMapNatural()); 084 suite.addTest(testsForConcurrentSkipListMapWithComparator()); 085 return suite; 086 } 087 088 protected Collection<Method> suppressForCheckedMap() { 089 return emptySet(); 090 } 091 092 protected Collection<Method> suppressForCheckedNavigableMap() { 093 return emptySet(); 094 } 095 096 protected Collection<Method> suppressForCheckedSortedMap() { 097 return emptySet(); 098 } 099 100 protected Collection<Method> suppressForEmptyMap() { 101 return emptySet(); 102 } 103 104 private static Collection<Method> suppressForEmptyNavigableMap() { 105 return emptySet(); 106 } 107 108 private static Collection<Method> suppressForEmptySortedMap() { 109 return emptySet(); 110 } 111 112 protected Collection<Method> suppressForSingletonMap() { 113 return emptySet(); 114 } 115 116 protected Collection<Method> suppressForHashMap() { 117 return emptySet(); 118 } 119 120 protected Collection<Method> suppressForHashtable() { 121 return emptySet(); 122 } 123 124 protected Collection<Method> suppressForLinkedHashMap() { 125 return emptySet(); 126 } 127 128 protected Collection<Method> suppressForSynchronizedNavigableMap() { 129 return emptySet(); 130 } 131 132 protected Collection<Method> suppressForTreeMapNatural() { 133 return emptySet(); 134 } 135 136 protected Collection<Method> suppressForTreeMapWithComparator() { 137 return emptySet(); 138 } 139 140 protected Collection<Method> suppressForUnmodifiableMap() { 141 return emptySet(); 142 } 143 144 protected Collection<Method> suppressForUnmodifiableNavigableMap() { 145 return emptySet(); 146 } 147 148 protected Collection<Method> suppressForUnmodifiableSortedMap() { 149 return emptySet(); 150 } 151 152 protected Collection<Method> suppressForEnumMap() { 153 return emptySet(); 154 } 155 156 protected Collection<Method> suppressForConcurrentHashMap() { 157 return emptySet(); 158 } 159 160 protected Collection<Method> suppressForConcurrentSkipListMap() { 161 return asList( 162 MapEntrySetTester.getSetValueMethod(), 163 MapEntrySetTester.getSetValueWithNullValuesAbsentMethod(), 164 MapEntrySetTester.getSetValueWithNullValuesPresentMethod(), 165 MapEntrySetTester.getIteratorSetValueAndRemove()); 166 } 167 168 public Test testsForCheckedMap() { 169 return MapTestSuiteBuilder.using( 170 new TestStringMapGenerator() { 171 @Override 172 protected Map<String, String> create(Entry<String, String>[] entries) { 173 Map<String, String> map = populate(new HashMap<String, String>(), entries); 174 return Collections.checkedMap(map, String.class, String.class); 175 } 176 }) 177 .named("checkedMap/HashMap") 178 .withFeatures( 179 MapFeature.GENERAL_PURPOSE, 180 MapFeature.ALLOWS_NULL_KEYS, 181 MapFeature.ALLOWS_NULL_VALUES, 182 MapFeature.ALLOWS_ANY_NULL_QUERIES, 183 MapFeature.FAILS_FAST_ON_CONCURRENT_MODIFICATION, 184 MapFeature.RESTRICTS_KEYS, 185 MapFeature.RESTRICTS_VALUES, 186 CollectionFeature.SUPPORTS_ITERATOR_REMOVE, 187 CollectionFeature.SERIALIZABLE, 188 CollectionSize.ANY) 189 .suppressing(suppressForCheckedMap()) 190 .createTestSuite(); 191 } 192 193 public Test testsForCheckedNavigableMap() { 194 return SortedMapTestSuiteBuilder.using( 195 new TestStringSortedMapGenerator() { 196 @Override 197 protected NavigableMap<String, String> create(Entry<String, String>[] entries) { 198 NavigableMap<String, String> map = populate(new TreeMap<String, String>(), entries); 199 return Collections.checkedNavigableMap(map, String.class, String.class); 200 } 201 }) 202 .named("checkedNavigableMap/TreeMap, natural") 203 .withFeatures( 204 MapFeature.GENERAL_PURPOSE, 205 MapFeature.ALLOWS_NULL_VALUES, 206 MapFeature.FAILS_FAST_ON_CONCURRENT_MODIFICATION, 207 MapFeature.RESTRICTS_KEYS, 208 MapFeature.RESTRICTS_VALUES, 209 CollectionFeature.KNOWN_ORDER, 210 CollectionFeature.SUPPORTS_ITERATOR_REMOVE, 211 CollectionFeature.SERIALIZABLE, 212 CollectionSize.ANY) 213 .suppressing(suppressForCheckedNavigableMap()) 214 .createTestSuite(); 215 } 216 217 public Test testsForCheckedSortedMap() { 218 return SortedMapTestSuiteBuilder.using( 219 new TestStringSortedMapGenerator() { 220 @Override 221 protected SortedMap<String, String> create(Entry<String, String>[] entries) { 222 SortedMap<String, String> map = populate(new TreeMap<String, String>(), entries); 223 return Collections.checkedSortedMap(map, String.class, String.class); 224 } 225 }) 226 .named("checkedSortedMap/TreeMap, natural") 227 .withFeatures( 228 MapFeature.GENERAL_PURPOSE, 229 MapFeature.ALLOWS_NULL_VALUES, 230 MapFeature.FAILS_FAST_ON_CONCURRENT_MODIFICATION, 231 MapFeature.RESTRICTS_KEYS, 232 MapFeature.RESTRICTS_VALUES, 233 CollectionFeature.KNOWN_ORDER, 234 CollectionFeature.SUPPORTS_ITERATOR_REMOVE, 235 CollectionFeature.SERIALIZABLE, 236 CollectionSize.ANY) 237 .suppressing(suppressForCheckedSortedMap()) 238 .createTestSuite(); 239 } 240 241 public Test testsForEmptyMap() { 242 return MapTestSuiteBuilder.using( 243 new TestStringMapGenerator() { 244 @Override 245 protected Map<String, String> create(Entry<String, String>[] entries) { 246 return emptyMap(); 247 } 248 }) 249 .named("emptyMap") 250 .withFeatures(CollectionFeature.SERIALIZABLE, CollectionSize.ZERO) 251 .suppressing(suppressForEmptyMap()) 252 .createTestSuite(); 253 } 254 255 public Test testsForEmptyNavigableMap() { 256 return MapTestSuiteBuilder.using( 257 new TestStringSortedMapGenerator() { 258 @Override 259 protected NavigableMap<String, String> create(Entry<String, String>[] entries) { 260 return Collections.emptyNavigableMap(); 261 } 262 }) 263 .named("emptyNavigableMap") 264 .withFeatures(CollectionFeature.SERIALIZABLE, CollectionSize.ZERO) 265 .suppressing(suppressForEmptyNavigableMap()) 266 .createTestSuite(); 267 } 268 269 public Test testsForEmptySortedMap() { 270 return MapTestSuiteBuilder.using( 271 new TestStringSortedMapGenerator() { 272 @Override 273 protected SortedMap<String, String> create(Entry<String, String>[] entries) { 274 return Collections.emptySortedMap(); 275 } 276 }) 277 .named("emptySortedMap") 278 .withFeatures(CollectionFeature.SERIALIZABLE, CollectionSize.ZERO) 279 .suppressing(suppressForEmptySortedMap()) 280 .createTestSuite(); 281 } 282 283 public Test testsForSingletonMap() { 284 return MapTestSuiteBuilder.using( 285 new TestStringMapGenerator() { 286 @Override 287 protected Map<String, String> create(Entry<String, String>[] entries) { 288 return singletonMap(entries[0].getKey(), entries[0].getValue()); 289 } 290 }) 291 .named("singletonMap") 292 .withFeatures( 293 MapFeature.ALLOWS_NULL_KEYS, 294 MapFeature.ALLOWS_NULL_VALUES, 295 MapFeature.ALLOWS_ANY_NULL_QUERIES, 296 CollectionFeature.SERIALIZABLE, 297 CollectionSize.ONE) 298 .suppressing(suppressForSingletonMap()) 299 .createTestSuite(); 300 } 301 302 public Test testsForHashMap() { 303 return MapTestSuiteBuilder.using( 304 new TestStringMapGenerator() { 305 @Override 306 protected Map<String, String> create(Entry<String, String>[] entries) { 307 return toHashMap(entries); 308 } 309 }) 310 .named("HashMap") 311 .withFeatures( 312 MapFeature.GENERAL_PURPOSE, 313 MapFeature.ALLOWS_NULL_KEYS, 314 MapFeature.ALLOWS_NULL_VALUES, 315 MapFeature.ALLOWS_ANY_NULL_QUERIES, 316 MapFeature.FAILS_FAST_ON_CONCURRENT_MODIFICATION, 317 CollectionFeature.SUPPORTS_ITERATOR_REMOVE, 318 CollectionFeature.SERIALIZABLE, 319 CollectionSize.ANY) 320 .suppressing(suppressForHashMap()) 321 .createTestSuite(); 322 } 323 324 public Test testsForHashtable() { 325 return MapTestSuiteBuilder.using( 326 new TestStringMapGenerator() { 327 @Override 328 // We are testing Hashtable / testing our tests on Hashtable. 329 @SuppressWarnings("JdkObsolete") 330 protected Map<String, String> create(Entry<String, String>[] entries) { 331 return populate(new Hashtable<String, String>(), entries); 332 } 333 }) 334 .withFeatures( 335 MapFeature.GENERAL_PURPOSE, 336 MapFeature.FAILS_FAST_ON_CONCURRENT_MODIFICATION, 337 MapFeature.RESTRICTS_KEYS, 338 MapFeature.SUPPORTS_REMOVE, 339 CollectionFeature.FAILS_FAST_ON_CONCURRENT_MODIFICATION, 340 CollectionFeature.SERIALIZABLE, 341 CollectionFeature.SUPPORTS_ITERATOR_REMOVE, 342 CollectionFeature.SUPPORTS_REMOVE, 343 CollectionSize.ANY) 344 .named("Hashtable") 345 .suppressing(suppressForHashtable()) 346 .createTestSuite(); 347 } 348 349 public Test testsForLinkedHashMap() { 350 return MapTestSuiteBuilder.using( 351 new TestStringMapGenerator() { 352 @Override 353 protected Map<String, String> create(Entry<String, String>[] entries) { 354 return populate(new LinkedHashMap<String, String>(), entries); 355 } 356 }) 357 .named("LinkedHashMap") 358 .withFeatures( 359 MapFeature.GENERAL_PURPOSE, 360 MapFeature.ALLOWS_NULL_KEYS, 361 MapFeature.ALLOWS_NULL_VALUES, 362 MapFeature.ALLOWS_ANY_NULL_QUERIES, 363 MapFeature.FAILS_FAST_ON_CONCURRENT_MODIFICATION, 364 CollectionFeature.SUPPORTS_ITERATOR_REMOVE, 365 CollectionFeature.KNOWN_ORDER, 366 CollectionFeature.SERIALIZABLE, 367 CollectionSize.ANY) 368 .suppressing(suppressForLinkedHashMap()) 369 .createTestSuite(); 370 } 371 372 /** 373 * Tests regular NavigableMap behavior of synchronizedNavigableMap(treeMap); does not test the 374 * fact that it's synchronized. 375 */ 376 public Test testsForSynchronizedNavigableMap() { 377 return NavigableMapTestSuiteBuilder.using( 378 new TestStringSortedMapGenerator() { 379 @Override 380 protected SortedMap<String, String> create(Entry<String, String>[] entries) { 381 NavigableMap<String, String> delegate = populate(new TreeMap<>(), entries); 382 return Collections.synchronizedNavigableMap(delegate); 383 } 384 }) 385 .named("synchronizedNavigableMap/TreeMap, natural") 386 .withFeatures( 387 MapFeature.GENERAL_PURPOSE, 388 MapFeature.ALLOWS_NULL_VALUES, 389 MapFeature.FAILS_FAST_ON_CONCURRENT_MODIFICATION, 390 CollectionFeature.SUPPORTS_ITERATOR_REMOVE, 391 CollectionFeature.KNOWN_ORDER, 392 CollectionFeature.SERIALIZABLE, 393 CollectionSize.ANY) 394 .suppressing(suppressForSynchronizedNavigableMap()) 395 .createTestSuite(); 396 } 397 398 public Test testsForTreeMapNatural() { 399 return NavigableMapTestSuiteBuilder.using( 400 new TestStringSortedMapGenerator() { 401 @Override 402 protected SortedMap<String, String> create(Entry<String, String>[] entries) { 403 /* 404 * TODO(cpovirk): it would be nice to create an input Map and use 405 * the copy constructor here and in the other tests 406 */ 407 return populate(new TreeMap<String, String>(), entries); 408 } 409 }) 410 .named("TreeMap, natural") 411 .withFeatures( 412 MapFeature.GENERAL_PURPOSE, 413 MapFeature.ALLOWS_NULL_VALUES, 414 MapFeature.FAILS_FAST_ON_CONCURRENT_MODIFICATION, 415 CollectionFeature.SUPPORTS_ITERATOR_REMOVE, 416 CollectionFeature.KNOWN_ORDER, 417 CollectionFeature.SERIALIZABLE, 418 CollectionSize.ANY) 419 .suppressing(suppressForTreeMapNatural()) 420 .createTestSuite(); 421 } 422 423 public Test testsForTreeMapWithComparator() { 424 return NavigableMapTestSuiteBuilder.using( 425 new TestStringSortedMapGenerator() { 426 @Override 427 protected SortedMap<String, String> create(Entry<String, String>[] entries) { 428 return populate( 429 new TreeMap<String, String>(arbitraryNullFriendlyComparator()), entries); 430 } 431 }) 432 .named("TreeMap, with comparator") 433 .withFeatures( 434 MapFeature.GENERAL_PURPOSE, 435 MapFeature.ALLOWS_NULL_KEYS, 436 MapFeature.ALLOWS_NULL_VALUES, 437 MapFeature.ALLOWS_ANY_NULL_QUERIES, 438 MapFeature.FAILS_FAST_ON_CONCURRENT_MODIFICATION, 439 CollectionFeature.SUPPORTS_ITERATOR_REMOVE, 440 CollectionFeature.KNOWN_ORDER, 441 CollectionFeature.SERIALIZABLE, 442 CollectionSize.ANY) 443 .suppressing(suppressForTreeMapWithComparator()) 444 .createTestSuite(); 445 } 446 447 public Test testsForUnmodifiableMap() { 448 return MapTestSuiteBuilder.using( 449 new TestStringMapGenerator() { 450 @Override 451 protected Map<String, String> create(Entry<String, String>[] entries) { 452 return unmodifiableMap(toHashMap(entries)); 453 } 454 }) 455 .named("unmodifiableMap/HashMap") 456 .withFeatures( 457 MapFeature.ALLOWS_NULL_KEYS, 458 MapFeature.ALLOWS_NULL_VALUES, 459 MapFeature.ALLOWS_ANY_NULL_QUERIES, 460 CollectionFeature.SERIALIZABLE, 461 CollectionSize.ANY) 462 .suppressing(suppressForUnmodifiableMap()) 463 .createTestSuite(); 464 } 465 466 public Test testsForUnmodifiableNavigableMap() { 467 return MapTestSuiteBuilder.using( 468 new TestStringSortedMapGenerator() { 469 @Override 470 protected NavigableMap<String, String> create(Entry<String, String>[] entries) { 471 return Collections.unmodifiableNavigableMap(populate(new TreeMap<>(), entries)); 472 } 473 }) 474 .named("unmodifiableNavigableMap/TreeMap, natural") 475 .withFeatures( 476 MapFeature.ALLOWS_NULL_VALUES, 477 CollectionFeature.KNOWN_ORDER, 478 CollectionFeature.SERIALIZABLE, 479 CollectionSize.ANY) 480 .suppressing(suppressForUnmodifiableNavigableMap()) 481 .createTestSuite(); 482 } 483 484 public Test testsForUnmodifiableSortedMap() { 485 return MapTestSuiteBuilder.using( 486 new TestStringSortedMapGenerator() { 487 @Override 488 protected SortedMap<String, String> create(Entry<String, String>[] entries) { 489 SortedMap<String, String> map = populate(new TreeMap<String, String>(), entries); 490 return Collections.unmodifiableSortedMap(map); 491 } 492 }) 493 .named("unmodifiableSortedMap/TreeMap, natural") 494 .withFeatures( 495 MapFeature.ALLOWS_NULL_VALUES, 496 CollectionFeature.KNOWN_ORDER, 497 CollectionFeature.SERIALIZABLE, 498 CollectionSize.ANY) 499 .suppressing(suppressForUnmodifiableSortedMap()) 500 .createTestSuite(); 501 } 502 503 public Test testsForEnumMap() { 504 return MapTestSuiteBuilder.using( 505 new TestEnumMapGenerator() { 506 @Override 507 protected Map<AnEnum, String> create(Entry<AnEnum, String>[] entries) { 508 return populate(new EnumMap<AnEnum, String>(AnEnum.class), entries); 509 } 510 }) 511 .named("EnumMap") 512 .withFeatures( 513 MapFeature.GENERAL_PURPOSE, 514 MapFeature.ALLOWS_NULL_VALUES, 515 MapFeature.RESTRICTS_KEYS, 516 CollectionFeature.SUPPORTS_ITERATOR_REMOVE, 517 CollectionFeature.KNOWN_ORDER, 518 CollectionFeature.SERIALIZABLE, 519 CollectionSize.ANY) 520 .suppressing(suppressForEnumMap()) 521 .createTestSuite(); 522 } 523 524 public Test testsForConcurrentHashMap() { 525 return MapTestSuiteBuilder.using( 526 new TestStringMapGenerator() { 527 @Override 528 protected Map<String, String> create(Entry<String, String>[] entries) { 529 return populate(new ConcurrentHashMap<String, String>(), entries); 530 } 531 }) 532 .named("ConcurrentHashMap") 533 .withFeatures( 534 MapFeature.GENERAL_PURPOSE, 535 CollectionFeature.SUPPORTS_ITERATOR_REMOVE, 536 CollectionFeature.SERIALIZABLE, 537 CollectionSize.ANY) 538 .suppressing(suppressForConcurrentHashMap()) 539 .createTestSuite(); 540 } 541 542 public Test testsForConcurrentSkipListMapNatural() { 543 return NavigableMapTestSuiteBuilder.using( 544 new TestStringSortedMapGenerator() { 545 @Override 546 protected SortedMap<String, String> create(Entry<String, String>[] entries) { 547 return populate(new ConcurrentSkipListMap<String, String>(), entries); 548 } 549 }) 550 .named("ConcurrentSkipListMap, natural") 551 .withFeatures( 552 MapFeature.GENERAL_PURPOSE, 553 CollectionFeature.SUPPORTS_ITERATOR_REMOVE, 554 CollectionFeature.KNOWN_ORDER, 555 CollectionFeature.SERIALIZABLE, 556 CollectionSize.ANY) 557 .suppressing(suppressForConcurrentSkipListMap()) 558 .createTestSuite(); 559 } 560 561 public Test testsForConcurrentSkipListMapWithComparator() { 562 return NavigableMapTestSuiteBuilder.using( 563 new TestStringSortedMapGenerator() { 564 @Override 565 protected SortedMap<String, String> create(Entry<String, String>[] entries) { 566 return populate( 567 new ConcurrentSkipListMap<String, String>(arbitraryNullFriendlyComparator()), 568 entries); 569 } 570 }) 571 .named("ConcurrentSkipListMap, with comparator") 572 .withFeatures( 573 MapFeature.GENERAL_PURPOSE, 574 CollectionFeature.SUPPORTS_ITERATOR_REMOVE, 575 CollectionFeature.KNOWN_ORDER, 576 CollectionFeature.SERIALIZABLE, 577 CollectionSize.ANY) 578 .suppressing(suppressForConcurrentSkipListMap()) 579 .createTestSuite(); 580 } 581 582 // TODO: IdentityHashMap, AbstractMap 583 584 private static Map<String, String> toHashMap(Entry<String, String>[] entries) { 585 return populate(new HashMap<String, String>(), entries); 586 } 587 588 // TODO: call conversion constructors or factory methods instead of using 589 // populate() on an empty map 590 @CanIgnoreReturnValue 591 private static <T, M extends Map<T, String>> M populate(M map, Entry<T, String>[] entries) { 592 for (Entry<T, String> entry : entries) { 593 map.put(entry.getKey(), entry.getValue()); 594 } 595 return map; 596 } 597 598 static <T> Comparator<T> arbitraryNullFriendlyComparator() { 599 return new NullFriendlyComparator<>(); 600 } 601 602 private static final class NullFriendlyComparator<T> implements Comparator<T>, Serializable { 603 @Override 604 public int compare(T left, T right) { 605 return String.valueOf(left).compareTo(String.valueOf(right)); 606 } 607 } 608}