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