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 Collection<Method> suppressForEmptyNavigableMap() { 105 return emptySet(); 106 } 107 108 private 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 } 166 167 public Test testsForCheckedMap() { 168 return MapTestSuiteBuilder.using( 169 new TestStringMapGenerator() { 170 @Override 171 protected Map<String, String> create(Entry<String, String>[] entries) { 172 Map<String, String> map = populate(new HashMap<String, String>(), entries); 173 return Collections.checkedMap(map, String.class, String.class); 174 } 175 }) 176 .named("checkedMap/HashMap") 177 .withFeatures( 178 MapFeature.GENERAL_PURPOSE, 179 MapFeature.ALLOWS_NULL_KEYS, 180 MapFeature.ALLOWS_NULL_VALUES, 181 MapFeature.ALLOWS_ANY_NULL_QUERIES, 182 MapFeature.FAILS_FAST_ON_CONCURRENT_MODIFICATION, 183 MapFeature.RESTRICTS_KEYS, 184 MapFeature.RESTRICTS_VALUES, 185 CollectionFeature.SUPPORTS_ITERATOR_REMOVE, 186 CollectionFeature.SERIALIZABLE, 187 CollectionSize.ANY) 188 .suppressing(suppressForCheckedMap()) 189 .createTestSuite(); 190 } 191 192 public Test testsForCheckedNavigableMap() { 193 return SortedMapTestSuiteBuilder.using( 194 new TestStringSortedMapGenerator() { 195 @Override 196 protected NavigableMap<String, String> create(Entry<String, String>[] entries) { 197 NavigableMap<String, String> map = populate(new TreeMap<String, String>(), entries); 198 return Collections.checkedNavigableMap(map, String.class, String.class); 199 } 200 }) 201 .named("checkedNavigableMap/TreeMap, natural") 202 .withFeatures( 203 MapFeature.GENERAL_PURPOSE, 204 MapFeature.ALLOWS_NULL_VALUES, 205 MapFeature.FAILS_FAST_ON_CONCURRENT_MODIFICATION, 206 MapFeature.RESTRICTS_KEYS, 207 MapFeature.RESTRICTS_VALUES, 208 CollectionFeature.KNOWN_ORDER, 209 CollectionFeature.SUPPORTS_ITERATOR_REMOVE, 210 CollectionFeature.SERIALIZABLE, 211 CollectionSize.ANY) 212 .suppressing(suppressForCheckedNavigableMap()) 213 .createTestSuite(); 214 } 215 216 public Test testsForCheckedSortedMap() { 217 return SortedMapTestSuiteBuilder.using( 218 new TestStringSortedMapGenerator() { 219 @Override 220 protected SortedMap<String, String> create(Entry<String, String>[] entries) { 221 SortedMap<String, String> map = populate(new TreeMap<String, String>(), entries); 222 return Collections.checkedSortedMap(map, String.class, String.class); 223 } 224 }) 225 .named("checkedSortedMap/TreeMap, natural") 226 .withFeatures( 227 MapFeature.GENERAL_PURPOSE, 228 MapFeature.ALLOWS_NULL_VALUES, 229 MapFeature.FAILS_FAST_ON_CONCURRENT_MODIFICATION, 230 MapFeature.RESTRICTS_KEYS, 231 MapFeature.RESTRICTS_VALUES, 232 CollectionFeature.KNOWN_ORDER, 233 CollectionFeature.SUPPORTS_ITERATOR_REMOVE, 234 CollectionFeature.SERIALIZABLE, 235 CollectionSize.ANY) 236 .suppressing(suppressForCheckedSortedMap()) 237 .createTestSuite(); 238 } 239 240 public Test testsForEmptyMap() { 241 return MapTestSuiteBuilder.using( 242 new TestStringMapGenerator() { 243 @Override 244 protected Map<String, String> create(Entry<String, String>[] entries) { 245 return emptyMap(); 246 } 247 }) 248 .named("emptyMap") 249 .withFeatures(CollectionFeature.SERIALIZABLE, CollectionSize.ZERO) 250 .suppressing(suppressForEmptyMap()) 251 .createTestSuite(); 252 } 253 254 public Test testsForEmptyNavigableMap() { 255 return MapTestSuiteBuilder.using( 256 new TestStringSortedMapGenerator() { 257 @Override 258 protected NavigableMap<String, String> create(Entry<String, String>[] entries) { 259 return Collections.emptyNavigableMap(); 260 } 261 }) 262 .named("emptyNavigableMap") 263 .withFeatures(CollectionFeature.SERIALIZABLE, CollectionSize.ZERO) 264 .suppressing(suppressForEmptyNavigableMap()) 265 .createTestSuite(); 266 } 267 268 public Test testsForEmptySortedMap() { 269 return MapTestSuiteBuilder.using( 270 new TestStringSortedMapGenerator() { 271 @Override 272 protected SortedMap<String, String> create(Entry<String, String>[] entries) { 273 return Collections.emptySortedMap(); 274 } 275 }) 276 .named("emptySortedMap") 277 .withFeatures(CollectionFeature.SERIALIZABLE, CollectionSize.ZERO) 278 .suppressing(suppressForEmptySortedMap()) 279 .createTestSuite(); 280 } 281 282 public Test testsForSingletonMap() { 283 return MapTestSuiteBuilder.using( 284 new TestStringMapGenerator() { 285 @Override 286 protected Map<String, String> create(Entry<String, String>[] entries) { 287 return singletonMap(entries[0].getKey(), entries[0].getValue()); 288 } 289 }) 290 .named("singletonMap") 291 .withFeatures( 292 MapFeature.ALLOWS_NULL_KEYS, 293 MapFeature.ALLOWS_NULL_VALUES, 294 MapFeature.ALLOWS_ANY_NULL_QUERIES, 295 CollectionFeature.SERIALIZABLE, 296 CollectionSize.ONE) 297 .suppressing(suppressForSingletonMap()) 298 .createTestSuite(); 299 } 300 301 public Test testsForHashMap() { 302 return MapTestSuiteBuilder.using( 303 new TestStringMapGenerator() { 304 @Override 305 protected Map<String, String> create(Entry<String, String>[] entries) { 306 return toHashMap(entries); 307 } 308 }) 309 .named("HashMap") 310 .withFeatures( 311 MapFeature.GENERAL_PURPOSE, 312 MapFeature.ALLOWS_NULL_KEYS, 313 MapFeature.ALLOWS_NULL_VALUES, 314 MapFeature.ALLOWS_ANY_NULL_QUERIES, 315 MapFeature.FAILS_FAST_ON_CONCURRENT_MODIFICATION, 316 CollectionFeature.SUPPORTS_ITERATOR_REMOVE, 317 CollectionFeature.SERIALIZABLE, 318 CollectionSize.ANY) 319 .suppressing(suppressForHashMap()) 320 .createTestSuite(); 321 } 322 323 public Test testsForHashtable() { 324 return MapTestSuiteBuilder.using( 325 new TestStringMapGenerator() { 326 @Override 327 // We are testing Hashtable / testing our tests on Hashtable. 328 @SuppressWarnings("JdkObsolete") 329 protected Map<String, String> create(Entry<String, String>[] entries) { 330 return populate(new Hashtable<String, String>(), entries); 331 } 332 }) 333 .withFeatures( 334 MapFeature.GENERAL_PURPOSE, 335 MapFeature.FAILS_FAST_ON_CONCURRENT_MODIFICATION, 336 MapFeature.RESTRICTS_KEYS, 337 MapFeature.SUPPORTS_REMOVE, 338 CollectionFeature.FAILS_FAST_ON_CONCURRENT_MODIFICATION, 339 CollectionFeature.SERIALIZABLE, 340 CollectionFeature.SUPPORTS_ITERATOR_REMOVE, 341 CollectionFeature.SUPPORTS_REMOVE, 342 CollectionSize.ANY) 343 .named("Hashtable") 344 .suppressing(suppressForHashtable()) 345 .createTestSuite(); 346 } 347 348 public Test testsForLinkedHashMap() { 349 return MapTestSuiteBuilder.using( 350 new TestStringMapGenerator() { 351 @Override 352 protected Map<String, String> create(Entry<String, String>[] entries) { 353 return populate(new LinkedHashMap<String, String>(), entries); 354 } 355 }) 356 .named("LinkedHashMap") 357 .withFeatures( 358 MapFeature.GENERAL_PURPOSE, 359 MapFeature.ALLOWS_NULL_KEYS, 360 MapFeature.ALLOWS_NULL_VALUES, 361 MapFeature.ALLOWS_ANY_NULL_QUERIES, 362 MapFeature.FAILS_FAST_ON_CONCURRENT_MODIFICATION, 363 CollectionFeature.SUPPORTS_ITERATOR_REMOVE, 364 CollectionFeature.KNOWN_ORDER, 365 CollectionFeature.SERIALIZABLE, 366 CollectionSize.ANY) 367 .suppressing(suppressForLinkedHashMap()) 368 .createTestSuite(); 369 } 370 371 /** 372 * Tests regular NavigableMap behavior of synchronizedNavigableMap(treeMap); does not test the 373 * fact that it's synchronized. 374 */ 375 public Test testsForSynchronizedNavigableMap() { 376 return NavigableMapTestSuiteBuilder.using( 377 new TestStringSortedMapGenerator() { 378 @Override 379 protected SortedMap<String, String> create(Entry<String, String>[] entries) { 380 NavigableMap<String, String> delegate = populate(new TreeMap<>(), entries); 381 return Collections.synchronizedNavigableMap(delegate); 382 } 383 }) 384 .named("synchronizedNavigableMap/TreeMap, natural") 385 .withFeatures( 386 MapFeature.GENERAL_PURPOSE, 387 MapFeature.ALLOWS_NULL_VALUES, 388 MapFeature.FAILS_FAST_ON_CONCURRENT_MODIFICATION, 389 CollectionFeature.SUPPORTS_ITERATOR_REMOVE, 390 CollectionFeature.KNOWN_ORDER, 391 CollectionFeature.SERIALIZABLE, 392 CollectionSize.ANY) 393 .suppressing(suppressForSynchronizedNavigableMap()) 394 .createTestSuite(); 395 } 396 397 public Test testsForTreeMapNatural() { 398 return NavigableMapTestSuiteBuilder.using( 399 new TestStringSortedMapGenerator() { 400 @Override 401 protected SortedMap<String, String> create(Entry<String, String>[] entries) { 402 /* 403 * TODO(cpovirk): it would be nice to create an input Map and use 404 * the copy constructor here and in the other tests 405 */ 406 return populate(new TreeMap<String, String>(), entries); 407 } 408 }) 409 .named("TreeMap, natural") 410 .withFeatures( 411 MapFeature.GENERAL_PURPOSE, 412 MapFeature.ALLOWS_NULL_VALUES, 413 MapFeature.FAILS_FAST_ON_CONCURRENT_MODIFICATION, 414 CollectionFeature.SUPPORTS_ITERATOR_REMOVE, 415 CollectionFeature.KNOWN_ORDER, 416 CollectionFeature.SERIALIZABLE, 417 CollectionSize.ANY) 418 .suppressing(suppressForTreeMapNatural()) 419 .createTestSuite(); 420 } 421 422 public Test testsForTreeMapWithComparator() { 423 return NavigableMapTestSuiteBuilder.using( 424 new TestStringSortedMapGenerator() { 425 @Override 426 protected SortedMap<String, String> create(Entry<String, String>[] entries) { 427 return populate( 428 new TreeMap<String, String>(arbitraryNullFriendlyComparator()), entries); 429 } 430 }) 431 .named("TreeMap, with comparator") 432 .withFeatures( 433 MapFeature.GENERAL_PURPOSE, 434 MapFeature.ALLOWS_NULL_KEYS, 435 MapFeature.ALLOWS_NULL_VALUES, 436 MapFeature.ALLOWS_ANY_NULL_QUERIES, 437 MapFeature.FAILS_FAST_ON_CONCURRENT_MODIFICATION, 438 CollectionFeature.SUPPORTS_ITERATOR_REMOVE, 439 CollectionFeature.KNOWN_ORDER, 440 CollectionFeature.SERIALIZABLE, 441 CollectionSize.ANY) 442 .suppressing(suppressForTreeMapWithComparator()) 443 .createTestSuite(); 444 } 445 446 public Test testsForUnmodifiableMap() { 447 return MapTestSuiteBuilder.using( 448 new TestStringMapGenerator() { 449 @Override 450 protected Map<String, String> create(Entry<String, String>[] entries) { 451 return unmodifiableMap(toHashMap(entries)); 452 } 453 }) 454 .named("unmodifiableMap/HashMap") 455 .withFeatures( 456 MapFeature.ALLOWS_NULL_KEYS, 457 MapFeature.ALLOWS_NULL_VALUES, 458 MapFeature.ALLOWS_ANY_NULL_QUERIES, 459 CollectionFeature.SERIALIZABLE, 460 CollectionSize.ANY) 461 .suppressing(suppressForUnmodifiableMap()) 462 .createTestSuite(); 463 } 464 465 public Test testsForUnmodifiableNavigableMap() { 466 return MapTestSuiteBuilder.using( 467 new TestStringSortedMapGenerator() { 468 @Override 469 protected NavigableMap<String, String> create(Entry<String, String>[] entries) { 470 return Collections.unmodifiableNavigableMap(populate(new TreeMap<>(), entries)); 471 } 472 }) 473 .named("unmodifiableNavigableMap/TreeMap, natural") 474 .withFeatures( 475 MapFeature.ALLOWS_NULL_VALUES, 476 CollectionFeature.KNOWN_ORDER, 477 CollectionFeature.SERIALIZABLE, 478 CollectionSize.ANY) 479 .suppressing(suppressForUnmodifiableNavigableMap()) 480 .createTestSuite(); 481 } 482 483 public Test testsForUnmodifiableSortedMap() { 484 return MapTestSuiteBuilder.using( 485 new TestStringSortedMapGenerator() { 486 @Override 487 protected SortedMap<String, String> create(Entry<String, String>[] entries) { 488 SortedMap<String, String> map = populate(new TreeMap<String, String>(), entries); 489 return Collections.unmodifiableSortedMap(map); 490 } 491 }) 492 .named("unmodifiableSortedMap/TreeMap, natural") 493 .withFeatures( 494 MapFeature.ALLOWS_NULL_VALUES, 495 CollectionFeature.KNOWN_ORDER, 496 CollectionFeature.SERIALIZABLE, 497 CollectionSize.ANY) 498 .suppressing(suppressForUnmodifiableSortedMap()) 499 .createTestSuite(); 500 } 501 502 public Test testsForEnumMap() { 503 return MapTestSuiteBuilder.using( 504 new TestEnumMapGenerator() { 505 @Override 506 protected Map<AnEnum, String> create(Entry<AnEnum, String>[] entries) { 507 return populate(new EnumMap<AnEnum, String>(AnEnum.class), entries); 508 } 509 }) 510 .named("EnumMap") 511 .withFeatures( 512 MapFeature.GENERAL_PURPOSE, 513 MapFeature.ALLOWS_NULL_VALUES, 514 MapFeature.RESTRICTS_KEYS, 515 CollectionFeature.SUPPORTS_ITERATOR_REMOVE, 516 CollectionFeature.KNOWN_ORDER, 517 CollectionFeature.SERIALIZABLE, 518 CollectionSize.ANY) 519 .suppressing(suppressForEnumMap()) 520 .createTestSuite(); 521 } 522 523 public Test testsForConcurrentHashMap() { 524 return MapTestSuiteBuilder.using( 525 new TestStringMapGenerator() { 526 @Override 527 protected Map<String, String> create(Entry<String, String>[] entries) { 528 return populate(new ConcurrentHashMap<String, String>(), entries); 529 } 530 }) 531 .named("ConcurrentHashMap") 532 .withFeatures( 533 MapFeature.GENERAL_PURPOSE, 534 CollectionFeature.SUPPORTS_ITERATOR_REMOVE, 535 CollectionFeature.SERIALIZABLE, 536 CollectionSize.ANY) 537 .suppressing(suppressForConcurrentHashMap()) 538 .createTestSuite(); 539 } 540 541 public Test testsForConcurrentSkipListMapNatural() { 542 return NavigableMapTestSuiteBuilder.using( 543 new TestStringSortedMapGenerator() { 544 @Override 545 protected SortedMap<String, String> create(Entry<String, String>[] entries) { 546 return populate(new ConcurrentSkipListMap<String, String>(), entries); 547 } 548 }) 549 .named("ConcurrentSkipListMap, natural") 550 .withFeatures( 551 MapFeature.GENERAL_PURPOSE, 552 CollectionFeature.SUPPORTS_ITERATOR_REMOVE, 553 CollectionFeature.KNOWN_ORDER, 554 CollectionFeature.SERIALIZABLE, 555 CollectionSize.ANY) 556 .suppressing(suppressForConcurrentSkipListMap()) 557 .createTestSuite(); 558 } 559 560 public Test testsForConcurrentSkipListMapWithComparator() { 561 return NavigableMapTestSuiteBuilder.using( 562 new TestStringSortedMapGenerator() { 563 @Override 564 protected SortedMap<String, String> create(Entry<String, String>[] entries) { 565 return populate( 566 new ConcurrentSkipListMap<String, String>(arbitraryNullFriendlyComparator()), 567 entries); 568 } 569 }) 570 .named("ConcurrentSkipListMap, with comparator") 571 .withFeatures( 572 MapFeature.GENERAL_PURPOSE, 573 CollectionFeature.SUPPORTS_ITERATOR_REMOVE, 574 CollectionFeature.KNOWN_ORDER, 575 CollectionFeature.SERIALIZABLE, 576 CollectionSize.ANY) 577 .suppressing(suppressForConcurrentSkipListMap()) 578 .createTestSuite(); 579 } 580 581 // TODO: IdentityHashMap, AbstractMap 582 583 private static Map<String, String> toHashMap(Entry<String, String>[] entries) { 584 return populate(new HashMap<String, String>(), entries); 585 } 586 587 // TODO: call conversion constructors or factory methods instead of using 588 // populate() on an empty map 589 @CanIgnoreReturnValue 590 private static <T, M extends Map<T, String>> M populate(M map, Entry<T, String>[] entries) { 591 for (Entry<T, String> entry : entries) { 592 map.put(entry.getKey(), entry.getValue()); 593 } 594 return map; 595 } 596 597 static <T> Comparator<T> arbitraryNullFriendlyComparator() { 598 return new NullFriendlyComparator<>(); 599 } 600 601 private static final class NullFriendlyComparator<T> implements Comparator<T>, Serializable { 602 @Override 603 public int compare(T left, T right) { 604 return String.valueOf(left).compareTo(String.valueOf(right)); 605 } 606 } 607}