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; 020 021import com.google.common.annotations.GwtIncompatible; 022import com.google.common.collect.testing.features.CollectionFeature; 023import com.google.common.collect.testing.features.CollectionSize; 024import com.google.common.collect.testing.features.MapFeature; 025import com.google.common.collect.testing.testers.MapEntrySetTester; 026import java.io.Serializable; 027import java.lang.reflect.Method; 028import java.util.Collection; 029import java.util.Collections; 030import java.util.Comparator; 031import java.util.EnumMap; 032import java.util.HashMap; 033import java.util.Hashtable; 034import java.util.LinkedHashMap; 035import java.util.Map; 036import java.util.Map.Entry; 037import java.util.NavigableMap; 038import java.util.SortedMap; 039import java.util.TreeMap; 040import java.util.concurrent.ConcurrentHashMap; 041import java.util.concurrent.ConcurrentSkipListMap; 042import junit.framework.Test; 043import junit.framework.TestSuite; 044 045/** 046 * Generates a test suite covering the {@link Map} implementations in the {@link java.util} package. 047 * Can be subclassed to specify tests that should be suppressed. 048 * 049 * @author Kevin Bourrillion 050 */ 051@GwtIncompatible 052public class TestsForMapsInJavaUtil { 053 054 public static Test suite() { 055 return new TestsForMapsInJavaUtil().allTests(); 056 } 057 058 public Test allTests() { 059 TestSuite suite = new TestSuite("java.util Maps"); 060 suite.addTest(testsForCheckedMap()); 061 suite.addTest(testsForCheckedNavigableMap()); 062 suite.addTest(testsForCheckedSortedMap()); 063 suite.addTest(testsForEmptyMap()); 064 suite.addTest(testsForEmptyNavigableMap()); 065 suite.addTest(testsForEmptySortedMap()); 066 suite.addTest(testsForSingletonMap()); 067 suite.addTest(testsForHashMap()); 068 suite.addTest(testsForHashtable()); 069 suite.addTest(testsForLinkedHashMap()); 070 suite.addTest(testsForSynchronizedNavigableMap()); 071 suite.addTest(testsForTreeMapNatural()); 072 suite.addTest(testsForTreeMapWithComparator()); 073 suite.addTest(testsForUnmodifiableMap()); 074 suite.addTest(testsForUnmodifiableNavigableMap()); 075 suite.addTest(testsForUnmodifiableSortedMap()); 076 suite.addTest(testsForEnumMap()); 077 suite.addTest(testsForConcurrentHashMap()); 078 suite.addTest(testsForConcurrentSkipListMapNatural()); 079 suite.addTest(testsForConcurrentSkipListMapWithComparator()); 080 return suite; 081 } 082 083 protected Collection<Method> suppressForCheckedMap() { 084 return Collections.emptySet(); 085 } 086 087 protected Collection<Method> suppressForCheckedNavigableMap() { 088 return Collections.emptySet(); 089 } 090 091 protected Collection<Method> suppressForCheckedSortedMap() { 092 return Collections.emptySet(); 093 } 094 095 protected Collection<Method> suppressForEmptyMap() { 096 return Collections.emptySet(); 097 } 098 099 private Collection<Method> suppressForEmptyNavigableMap() { 100 return Collections.emptySet(); 101 } 102 103 private Collection<Method> suppressForEmptySortedMap() { 104 return Collections.emptySet(); 105 } 106 107 protected Collection<Method> suppressForSingletonMap() { 108 return Collections.emptySet(); 109 } 110 111 protected Collection<Method> suppressForHashMap() { 112 return Collections.emptySet(); 113 } 114 115 protected Collection<Method> suppressForHashtable() { 116 return Collections.emptySet(); 117 } 118 119 protected Collection<Method> suppressForLinkedHashMap() { 120 return Collections.emptySet(); 121 } 122 123 protected Collection<Method> suppressForSynchronizedNavigableMap() { 124 return Collections.emptySet(); 125 } 126 127 protected Collection<Method> suppressForTreeMapNatural() { 128 return Collections.emptySet(); 129 } 130 131 protected Collection<Method> suppressForTreeMapWithComparator() { 132 return Collections.emptySet(); 133 } 134 135 protected Collection<Method> suppressForUnmodifiableMap() { 136 return Collections.emptySet(); 137 } 138 139 protected Collection<Method> suppressForUnmodifiableNavigableMap() { 140 return Collections.emptySet(); 141 } 142 143 protected Collection<Method> suppressForUnmodifiableSortedMap() { 144 return Collections.emptySet(); 145 } 146 147 protected Collection<Method> suppressForEnumMap() { 148 return Collections.emptySet(); 149 } 150 151 protected Collection<Method> suppressForConcurrentHashMap() { 152 return Collections.emptySet(); 153 } 154 155 protected Collection<Method> suppressForConcurrentSkipListMap() { 156 return asList( 157 MapEntrySetTester.getSetValueMethod(), 158 MapEntrySetTester.getSetValueWithNullValuesAbsentMethod(), 159 MapEntrySetTester.getSetValueWithNullValuesPresentMethod()); 160 } 161 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}