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