1 /* 2 * Copyright (C) 2009 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.testing; 18 19 import static java.util.Arrays.asList; 20 21 import com.google.common.annotations.GwtIncompatible; 22 import com.google.common.collect.testing.features.CollectionFeature; 23 import com.google.common.collect.testing.features.CollectionSize; 24 import com.google.common.collect.testing.features.MapFeature; 25 import com.google.common.collect.testing.testers.MapEntrySetTester; 26 import java.io.Serializable; 27 import java.lang.reflect.Method; 28 import java.util.Collection; 29 import java.util.Collections; 30 import java.util.Comparator; 31 import java.util.EnumMap; 32 import java.util.HashMap; 33 import java.util.Hashtable; 34 import java.util.LinkedHashMap; 35 import java.util.Map; 36 import java.util.Map.Entry; 37 import java.util.NavigableMap; 38 import java.util.SortedMap; 39 import java.util.TreeMap; 40 import java.util.concurrent.ConcurrentHashMap; 41 import java.util.concurrent.ConcurrentSkipListMap; 42 import junit.framework.Test; 43 import junit.framework.TestSuite; 44 45 /** 46 * Generates a test suite covering the {@link Map} implementations in the {@link java.util} package. 47 * Can be subclassed to specify tests that should be suppressed. 48 * 49 * @author Kevin Bourrillion 50 */ 51 @GwtIncompatible 52 public class TestsForMapsInJavaUtil { 53 suite()54 public static Test suite() { 55 return new TestsForMapsInJavaUtil().allTests(); 56 } 57 allTests()58 public Test allTests() { 59 TestSuite suite = new TestSuite("java.util Maps"); 60 suite.addTest(testsForCheckedMap()); 61 suite.addTest(testsForCheckedNavigableMap()); 62 suite.addTest(testsForCheckedSortedMap()); 63 suite.addTest(testsForEmptyMap()); 64 suite.addTest(testsForEmptyNavigableMap()); 65 suite.addTest(testsForEmptySortedMap()); 66 suite.addTest(testsForSingletonMap()); 67 suite.addTest(testsForHashMap()); 68 suite.addTest(testsForHashtable()); 69 suite.addTest(testsForLinkedHashMap()); 70 suite.addTest(testsForSynchronizedNavigableMap()); 71 suite.addTest(testsForTreeMapNatural()); 72 suite.addTest(testsForTreeMapWithComparator()); 73 suite.addTest(testsForUnmodifiableMap()); 74 suite.addTest(testsForUnmodifiableNavigableMap()); 75 suite.addTest(testsForUnmodifiableSortedMap()); 76 suite.addTest(testsForEnumMap()); 77 suite.addTest(testsForConcurrentHashMap()); 78 suite.addTest(testsForConcurrentSkipListMapNatural()); 79 suite.addTest(testsForConcurrentSkipListMapWithComparator()); 80 return suite; 81 } 82 suppressForCheckedMap()83 protected Collection<Method> suppressForCheckedMap() { 84 return Collections.emptySet(); 85 } 86 suppressForCheckedNavigableMap()87 protected Collection<Method> suppressForCheckedNavigableMap() { 88 return Collections.emptySet(); 89 } 90 suppressForCheckedSortedMap()91 protected Collection<Method> suppressForCheckedSortedMap() { 92 return Collections.emptySet(); 93 } 94 suppressForEmptyMap()95 protected Collection<Method> suppressForEmptyMap() { 96 return Collections.emptySet(); 97 } 98 suppressForEmptyNavigableMap()99 private Collection<Method> suppressForEmptyNavigableMap() { 100 return Collections.emptySet(); 101 } 102 suppressForEmptySortedMap()103 private Collection<Method> suppressForEmptySortedMap() { 104 return Collections.emptySet(); 105 } 106 suppressForSingletonMap()107 protected Collection<Method> suppressForSingletonMap() { 108 return Collections.emptySet(); 109 } 110 suppressForHashMap()111 protected Collection<Method> suppressForHashMap() { 112 return Collections.emptySet(); 113 } 114 suppressForHashtable()115 protected Collection<Method> suppressForHashtable() { 116 return Collections.emptySet(); 117 } 118 suppressForLinkedHashMap()119 protected Collection<Method> suppressForLinkedHashMap() { 120 return Collections.emptySet(); 121 } 122 suppressForSynchronizedNavigableMap()123 protected Collection<Method> suppressForSynchronizedNavigableMap() { 124 return Collections.emptySet(); 125 } 126 suppressForTreeMapNatural()127 protected Collection<Method> suppressForTreeMapNatural() { 128 return Collections.emptySet(); 129 } 130 suppressForTreeMapWithComparator()131 protected Collection<Method> suppressForTreeMapWithComparator() { 132 return Collections.emptySet(); 133 } 134 suppressForUnmodifiableMap()135 protected Collection<Method> suppressForUnmodifiableMap() { 136 return Collections.emptySet(); 137 } 138 suppressForUnmodifiableNavigableMap()139 protected Collection<Method> suppressForUnmodifiableNavigableMap() { 140 return Collections.emptySet(); 141 } 142 suppressForUnmodifiableSortedMap()143 protected Collection<Method> suppressForUnmodifiableSortedMap() { 144 return Collections.emptySet(); 145 } 146 suppressForEnumMap()147 protected Collection<Method> suppressForEnumMap() { 148 return Collections.emptySet(); 149 } 150 suppressForConcurrentHashMap()151 protected Collection<Method> suppressForConcurrentHashMap() { 152 return Collections.emptySet(); 153 } 154 suppressForConcurrentSkipListMap()155 protected Collection<Method> suppressForConcurrentSkipListMap() { 156 return asList( 157 MapEntrySetTester.getSetValueMethod(), 158 MapEntrySetTester.getSetValueWithNullValuesAbsentMethod(), 159 MapEntrySetTester.getSetValueWithNullValuesPresentMethod()); 160 } 161 testsForCheckedMap()162 public Test testsForCheckedMap() { 163 return MapTestSuiteBuilder.using( 164 new TestStringMapGenerator() { 165 @Override 166 protected Map<String, String> create(Entry<String, String>[] entries) { 167 Map<String, String> map = populate(new HashMap<String, String>(), entries); 168 return Collections.checkedMap(map, String.class, String.class); 169 } 170 }) 171 .named("checkedMap/HashMap") 172 .withFeatures( 173 MapFeature.GENERAL_PURPOSE, 174 MapFeature.ALLOWS_NULL_KEYS, 175 MapFeature.ALLOWS_NULL_VALUES, 176 MapFeature.ALLOWS_ANY_NULL_QUERIES, 177 MapFeature.FAILS_FAST_ON_CONCURRENT_MODIFICATION, 178 MapFeature.RESTRICTS_KEYS, 179 MapFeature.RESTRICTS_VALUES, 180 CollectionFeature.SUPPORTS_ITERATOR_REMOVE, 181 CollectionFeature.SERIALIZABLE, 182 CollectionSize.ANY) 183 .suppressing(suppressForCheckedMap()) 184 .createTestSuite(); 185 } 186 187 public Test testsForCheckedNavigableMap() { 188 return SortedMapTestSuiteBuilder.using( 189 new TestStringSortedMapGenerator() { 190 @Override 191 protected NavigableMap<String, String> create(Entry<String, String>[] entries) { 192 NavigableMap<String, String> map = populate(new TreeMap<String, String>(), entries); 193 return Collections.checkedNavigableMap(map, String.class, String.class); 194 } 195 }) 196 .named("checkedNavigableMap/TreeMap, natural") 197 .withFeatures( 198 MapFeature.GENERAL_PURPOSE, 199 MapFeature.ALLOWS_NULL_VALUES, 200 MapFeature.FAILS_FAST_ON_CONCURRENT_MODIFICATION, 201 MapFeature.RESTRICTS_KEYS, 202 MapFeature.RESTRICTS_VALUES, 203 CollectionFeature.KNOWN_ORDER, 204 CollectionFeature.SUPPORTS_ITERATOR_REMOVE, 205 CollectionFeature.SERIALIZABLE, 206 CollectionSize.ANY) 207 .suppressing(suppressForCheckedNavigableMap()) 208 .createTestSuite(); 209 } 210 211 public Test testsForCheckedSortedMap() { 212 return SortedMapTestSuiteBuilder.using( 213 new TestStringSortedMapGenerator() { 214 @Override 215 protected SortedMap<String, String> create(Entry<String, String>[] entries) { 216 SortedMap<String, String> map = populate(new TreeMap<String, String>(), entries); 217 return Collections.checkedSortedMap(map, String.class, String.class); 218 } 219 }) 220 .named("checkedSortedMap/TreeMap, natural") 221 .withFeatures( 222 MapFeature.GENERAL_PURPOSE, 223 MapFeature.ALLOWS_NULL_VALUES, 224 MapFeature.FAILS_FAST_ON_CONCURRENT_MODIFICATION, 225 MapFeature.RESTRICTS_KEYS, 226 MapFeature.RESTRICTS_VALUES, 227 CollectionFeature.KNOWN_ORDER, 228 CollectionFeature.SUPPORTS_ITERATOR_REMOVE, 229 CollectionFeature.SERIALIZABLE, 230 CollectionSize.ANY) 231 .suppressing(suppressForCheckedSortedMap()) 232 .createTestSuite(); 233 } 234 235 public Test testsForEmptyMap() { 236 return MapTestSuiteBuilder.using( 237 new TestStringMapGenerator() { 238 @Override 239 protected Map<String, String> create(Entry<String, String>[] entries) { 240 return Collections.emptyMap(); 241 } 242 }) 243 .named("emptyMap") 244 .withFeatures(CollectionFeature.SERIALIZABLE, CollectionSize.ZERO) 245 .suppressing(suppressForEmptyMap()) 246 .createTestSuite(); 247 } 248 249 public Test testsForEmptyNavigableMap() { 250 return MapTestSuiteBuilder.using( 251 new TestStringSortedMapGenerator() { 252 @Override 253 protected NavigableMap<String, String> create(Entry<String, String>[] entries) { 254 return Collections.emptyNavigableMap(); 255 } 256 }) 257 .named("emptyNavigableMap") 258 .withFeatures(CollectionFeature.SERIALIZABLE, CollectionSize.ZERO) 259 .suppressing(suppressForEmptyNavigableMap()) 260 .createTestSuite(); 261 } 262 263 public Test testsForEmptySortedMap() { 264 return MapTestSuiteBuilder.using( 265 new TestStringSortedMapGenerator() { 266 @Override 267 protected SortedMap<String, String> create(Entry<String, String>[] entries) { 268 return Collections.emptySortedMap(); 269 } 270 }) 271 .named("emptySortedMap") 272 .withFeatures(CollectionFeature.SERIALIZABLE, CollectionSize.ZERO) 273 .suppressing(suppressForEmptySortedMap()) 274 .createTestSuite(); 275 } 276 277 public Test testsForSingletonMap() { 278 return MapTestSuiteBuilder.using( 279 new TestStringMapGenerator() { 280 @Override 281 protected Map<String, String> create(Entry<String, String>[] entries) { 282 return Collections.singletonMap(entries[0].getKey(), entries[0].getValue()); 283 } 284 }) 285 .named("singletonMap") 286 .withFeatures( 287 MapFeature.ALLOWS_NULL_KEYS, 288 MapFeature.ALLOWS_NULL_VALUES, 289 MapFeature.ALLOWS_ANY_NULL_QUERIES, 290 CollectionFeature.SERIALIZABLE, 291 CollectionSize.ONE) 292 .suppressing(suppressForSingletonMap()) 293 .createTestSuite(); 294 } 295 296 public Test testsForHashMap() { 297 return MapTestSuiteBuilder.using( 298 new TestStringMapGenerator() { 299 @Override 300 protected Map<String, String> create(Entry<String, String>[] entries) { 301 return toHashMap(entries); 302 } 303 }) 304 .named("HashMap") 305 .withFeatures( 306 MapFeature.GENERAL_PURPOSE, 307 MapFeature.ALLOWS_NULL_KEYS, 308 MapFeature.ALLOWS_NULL_VALUES, 309 MapFeature.ALLOWS_ANY_NULL_QUERIES, 310 MapFeature.FAILS_FAST_ON_CONCURRENT_MODIFICATION, 311 CollectionFeature.SUPPORTS_ITERATOR_REMOVE, 312 CollectionFeature.SERIALIZABLE, 313 CollectionSize.ANY) 314 .suppressing(suppressForHashMap()) 315 .createTestSuite(); 316 } 317 318 public Test testsForHashtable() { 319 return MapTestSuiteBuilder.using( 320 new TestStringMapGenerator() { 321 @Override 322 protected Map<String, String> create(Entry<String, String>[] entries) { 323 return populate(new Hashtable<String, String>(), entries); 324 } 325 }) 326 .withFeatures( 327 MapFeature.GENERAL_PURPOSE, 328 MapFeature.FAILS_FAST_ON_CONCURRENT_MODIFICATION, 329 MapFeature.RESTRICTS_KEYS, 330 MapFeature.SUPPORTS_REMOVE, 331 CollectionFeature.FAILS_FAST_ON_CONCURRENT_MODIFICATION, 332 CollectionFeature.SERIALIZABLE, 333 CollectionFeature.SUPPORTS_ITERATOR_REMOVE, 334 CollectionFeature.SUPPORTS_REMOVE, 335 CollectionSize.ANY) 336 .named("Hashtable") 337 .suppressing(suppressForHashtable()) 338 .createTestSuite(); 339 } 340 341 public Test testsForLinkedHashMap() { 342 return MapTestSuiteBuilder.using( 343 new TestStringMapGenerator() { 344 @Override 345 protected Map<String, String> create(Entry<String, String>[] entries) { 346 return populate(new LinkedHashMap<String, String>(), entries); 347 } 348 }) 349 .named("LinkedHashMap") 350 .withFeatures( 351 MapFeature.GENERAL_PURPOSE, 352 MapFeature.ALLOWS_NULL_KEYS, 353 MapFeature.ALLOWS_NULL_VALUES, 354 MapFeature.ALLOWS_ANY_NULL_QUERIES, 355 MapFeature.FAILS_FAST_ON_CONCURRENT_MODIFICATION, 356 CollectionFeature.SUPPORTS_ITERATOR_REMOVE, 357 CollectionFeature.KNOWN_ORDER, 358 CollectionFeature.SERIALIZABLE, 359 CollectionSize.ANY) 360 .suppressing(suppressForLinkedHashMap()) 361 .createTestSuite(); 362 } 363 364 /** 365 * Tests regular NavigableMap behavior of synchronizedNavigableMap(treeMap); does not test the 366 * fact that it's synchronized. 367 */ 368 public Test testsForSynchronizedNavigableMap() { 369 return NavigableMapTestSuiteBuilder.using( 370 new TestStringSortedMapGenerator() { 371 @Override 372 protected SortedMap<String, String> create(Entry<String, String>[] entries) { 373 NavigableMap<String, String> delegate = populate(new TreeMap<>(), entries); 374 return Collections.synchronizedNavigableMap(delegate); 375 } 376 }) 377 .named("synchronizedNavigableMap/TreeMap, natural") 378 .withFeatures( 379 MapFeature.GENERAL_PURPOSE, 380 MapFeature.ALLOWS_NULL_VALUES, 381 MapFeature.FAILS_FAST_ON_CONCURRENT_MODIFICATION, 382 CollectionFeature.SUPPORTS_ITERATOR_REMOVE, 383 CollectionFeature.KNOWN_ORDER, 384 CollectionFeature.SERIALIZABLE, 385 CollectionSize.ANY) 386 .suppressing(suppressForSynchronizedNavigableMap()) 387 .createTestSuite(); 388 } 389 390 public Test testsForTreeMapNatural() { 391 return NavigableMapTestSuiteBuilder.using( 392 new TestStringSortedMapGenerator() { 393 @Override 394 protected SortedMap<String, String> create(Entry<String, String>[] entries) { 395 /* 396 * TODO(cpovirk): it would be nice to create an input Map and use 397 * the copy constructor here and in the other tests 398 */ 399 return populate(new TreeMap<String, String>(), entries); 400 } 401 }) 402 .named("TreeMap, natural") 403 .withFeatures( 404 MapFeature.GENERAL_PURPOSE, 405 MapFeature.ALLOWS_NULL_VALUES, 406 MapFeature.FAILS_FAST_ON_CONCURRENT_MODIFICATION, 407 CollectionFeature.SUPPORTS_ITERATOR_REMOVE, 408 CollectionFeature.KNOWN_ORDER, 409 CollectionFeature.SERIALIZABLE, 410 CollectionSize.ANY) 411 .suppressing(suppressForTreeMapNatural()) 412 .createTestSuite(); 413 } 414 415 public Test testsForTreeMapWithComparator() { 416 return NavigableMapTestSuiteBuilder.using( 417 new TestStringSortedMapGenerator() { 418 @Override 419 protected SortedMap<String, String> create(Entry<String, String>[] entries) { 420 return populate( 421 new TreeMap<String, String>(arbitraryNullFriendlyComparator()), entries); 422 } 423 }) 424 .named("TreeMap, with comparator") 425 .withFeatures( 426 MapFeature.GENERAL_PURPOSE, 427 MapFeature.ALLOWS_NULL_KEYS, 428 MapFeature.ALLOWS_NULL_VALUES, 429 MapFeature.ALLOWS_ANY_NULL_QUERIES, 430 MapFeature.FAILS_FAST_ON_CONCURRENT_MODIFICATION, 431 CollectionFeature.SUPPORTS_ITERATOR_REMOVE, 432 CollectionFeature.KNOWN_ORDER, 433 CollectionFeature.SERIALIZABLE, 434 CollectionSize.ANY) 435 .suppressing(suppressForTreeMapWithComparator()) 436 .createTestSuite(); 437 } 438 439 public Test testsForUnmodifiableMap() { 440 return MapTestSuiteBuilder.using( 441 new TestStringMapGenerator() { 442 @Override 443 protected Map<String, String> create(Entry<String, String>[] entries) { 444 return Collections.unmodifiableMap(toHashMap(entries)); 445 } 446 }) 447 .named("unmodifiableMap/HashMap") 448 .withFeatures( 449 MapFeature.ALLOWS_NULL_KEYS, 450 MapFeature.ALLOWS_NULL_VALUES, 451 MapFeature.ALLOWS_ANY_NULL_QUERIES, 452 CollectionFeature.SERIALIZABLE, 453 CollectionSize.ANY) 454 .suppressing(suppressForUnmodifiableMap()) 455 .createTestSuite(); 456 } 457 458 public Test testsForUnmodifiableNavigableMap() { 459 return MapTestSuiteBuilder.using( 460 new TestStringSortedMapGenerator() { 461 @Override 462 protected NavigableMap<String, String> create(Entry<String, String>[] entries) { 463 return Collections.unmodifiableNavigableMap(populate(new TreeMap<>(), entries)); 464 } 465 }) 466 .named("unmodifiableNavigableMap/TreeMap, natural") 467 .withFeatures( 468 MapFeature.ALLOWS_NULL_VALUES, 469 CollectionFeature.KNOWN_ORDER, 470 CollectionFeature.SERIALIZABLE, 471 CollectionSize.ANY) 472 .suppressing(suppressForUnmodifiableNavigableMap()) 473 .createTestSuite(); 474 } 475 476 public Test testsForUnmodifiableSortedMap() { 477 return MapTestSuiteBuilder.using( 478 new TestStringSortedMapGenerator() { 479 @Override 480 protected SortedMap<String, String> create(Entry<String, String>[] entries) { 481 SortedMap<String, String> map = populate(new TreeMap<String, String>(), entries); 482 return Collections.unmodifiableSortedMap(map); 483 } 484 }) 485 .named("unmodifiableSortedMap/TreeMap, natural") 486 .withFeatures( 487 MapFeature.ALLOWS_NULL_VALUES, 488 CollectionFeature.KNOWN_ORDER, 489 CollectionFeature.SERIALIZABLE, 490 CollectionSize.ANY) 491 .suppressing(suppressForUnmodifiableSortedMap()) 492 .createTestSuite(); 493 } 494 495 public Test testsForEnumMap() { 496 return MapTestSuiteBuilder.using( 497 new TestEnumMapGenerator() { 498 @Override 499 protected Map<AnEnum, String> create(Entry<AnEnum, String>[] entries) { 500 return populate(new EnumMap<AnEnum, String>(AnEnum.class), entries); 501 } 502 }) 503 .named("EnumMap") 504 .withFeatures( 505 MapFeature.GENERAL_PURPOSE, 506 MapFeature.ALLOWS_NULL_VALUES, 507 MapFeature.RESTRICTS_KEYS, 508 CollectionFeature.SUPPORTS_ITERATOR_REMOVE, 509 CollectionFeature.KNOWN_ORDER, 510 CollectionFeature.SERIALIZABLE, 511 CollectionSize.ANY) 512 .suppressing(suppressForEnumMap()) 513 .createTestSuite(); 514 } 515 516 public Test testsForConcurrentHashMap() { 517 return MapTestSuiteBuilder.using( 518 new TestStringMapGenerator() { 519 @Override 520 protected Map<String, String> create(Entry<String, String>[] entries) { 521 return populate(new ConcurrentHashMap<String, String>(), entries); 522 } 523 }) 524 .named("ConcurrentHashMap") 525 .withFeatures( 526 MapFeature.GENERAL_PURPOSE, 527 CollectionFeature.SUPPORTS_ITERATOR_REMOVE, 528 CollectionFeature.SERIALIZABLE, 529 CollectionSize.ANY) 530 .suppressing(suppressForConcurrentHashMap()) 531 .createTestSuite(); 532 } 533 534 public Test testsForConcurrentSkipListMapNatural() { 535 return NavigableMapTestSuiteBuilder.using( 536 new TestStringSortedMapGenerator() { 537 @Override 538 protected SortedMap<String, String> create(Entry<String, String>[] entries) { 539 return populate(new ConcurrentSkipListMap<String, String>(), entries); 540 } 541 }) 542 .named("ConcurrentSkipListMap, natural") 543 .withFeatures( 544 MapFeature.GENERAL_PURPOSE, 545 CollectionFeature.SUPPORTS_ITERATOR_REMOVE, 546 CollectionFeature.KNOWN_ORDER, 547 CollectionFeature.SERIALIZABLE, 548 CollectionSize.ANY) 549 .suppressing(suppressForConcurrentSkipListMap()) 550 .createTestSuite(); 551 } 552 553 public Test testsForConcurrentSkipListMapWithComparator() { 554 return NavigableMapTestSuiteBuilder.using( 555 new TestStringSortedMapGenerator() { 556 @Override 557 protected SortedMap<String, String> create(Entry<String, String>[] entries) { 558 return populate( 559 new ConcurrentSkipListMap<String, String>(arbitraryNullFriendlyComparator()), 560 entries); 561 } 562 }) 563 .named("ConcurrentSkipListMap, with comparator") 564 .withFeatures( 565 MapFeature.GENERAL_PURPOSE, 566 CollectionFeature.SUPPORTS_ITERATOR_REMOVE, 567 CollectionFeature.KNOWN_ORDER, 568 CollectionFeature.SERIALIZABLE, 569 CollectionSize.ANY) 570 .suppressing(suppressForConcurrentSkipListMap()) 571 .createTestSuite(); 572 } 573 574 // TODO: IdentityHashMap, AbstractMap 575 576 private static Map<String, String> toHashMap(Entry<String, String>[] entries) { 577 return populate(new HashMap<String, String>(), entries); 578 } 579 580 // TODO: call conversion constructors or factory methods instead of using 581 // populate() on an empty map 582 private static <T, M extends Map<T, String>> M populate(M map, Entry<T, String>[] entries) { 583 for (Entry<T, String> entry : entries) { 584 map.put(entry.getKey(), entry.getValue()); 585 } 586 return map; 587 } 588 589 static <T> Comparator<T> arbitraryNullFriendlyComparator() { 590 return new NullFriendlyComparator<T>(); 591 } 592 593 private static final class NullFriendlyComparator<T> implements Comparator<T>, Serializable { 594 @Override 595 public int compare(T left, T right) { 596 return String.valueOf(left).compareTo(String.valueOf(right)); 597 } 598 } 599 } 600