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.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(testsForCheckedSortedMap()); 066 suite.addTest(testsForEmptyMap()); 067 suite.addTest(testsForSingletonMap()); 068 suite.addTest(testsForHashMap()); 069 suite.addTest(testsForHashtable()); 070 suite.addTest(testsForLinkedHashMap()); 071 suite.addTest(testsForTreeMapNatural()); 072 suite.addTest(testsForTreeMapWithComparator()); 073 suite.addTest(testsForUnmodifiableMap()); 074 suite.addTest(testsForUnmodifiableSortedMap()); 075 suite.addTest(testsForEnumMap()); 076 suite.addTest(testsForConcurrentHashMap()); 077 suite.addTest(testsForConcurrentSkipListMapNatural()); 078 suite.addTest(testsForConcurrentSkipListMapWithComparator()); 079 return suite; 080 } 081 082 protected Collection<Method> suppressForCheckedMap() { 083 return emptySet(); 084 } 085 086 protected Collection<Method> suppressForCheckedSortedMap() { 087 return emptySet(); 088 } 089 090 protected Collection<Method> suppressForEmptyMap() { 091 return emptySet(); 092 } 093 094 protected Collection<Method> suppressForSingletonMap() { 095 return emptySet(); 096 } 097 098 protected Collection<Method> suppressForHashMap() { 099 return emptySet(); 100 } 101 102 protected Collection<Method> suppressForHashtable() { 103 return emptySet(); 104 } 105 106 protected Collection<Method> suppressForLinkedHashMap() { 107 return emptySet(); 108 } 109 110 protected Collection<Method> suppressForTreeMapNatural() { 111 return emptySet(); 112 } 113 114 protected Collection<Method> suppressForTreeMapWithComparator() { 115 return emptySet(); 116 } 117 118 protected Collection<Method> suppressForUnmodifiableMap() { 119 return emptySet(); 120 } 121 122 protected Collection<Method> suppressForUnmodifiableSortedMap() { 123 return emptySet(); 124 } 125 126 protected Collection<Method> suppressForEnumMap() { 127 return emptySet(); 128 } 129 130 protected Collection<Method> suppressForConcurrentHashMap() { 131 return emptySet(); 132 } 133 134 protected Collection<Method> suppressForConcurrentSkipListMap() { 135 return asList( 136 MapEntrySetTester.getSetValueMethod(), 137 MapEntrySetTester.getSetValueWithNullValuesAbsentMethod(), 138 MapEntrySetTester.getSetValueWithNullValuesPresentMethod()); 139 } 140 141 public Test testsForCheckedMap() { 142 return MapTestSuiteBuilder.using( 143 new TestStringMapGenerator() { 144 @Override 145 protected Map<String, String> create(Entry<String, String>[] entries) { 146 Map<String, String> map = populate(new HashMap<String, String>(), entries); 147 return Collections.checkedMap(map, String.class, String.class); 148 } 149 }) 150 .named("checkedMap/HashMap") 151 .withFeatures( 152 MapFeature.GENERAL_PURPOSE, 153 MapFeature.ALLOWS_NULL_KEYS, 154 MapFeature.ALLOWS_NULL_VALUES, 155 MapFeature.ALLOWS_ANY_NULL_QUERIES, 156 MapFeature.FAILS_FAST_ON_CONCURRENT_MODIFICATION, 157 MapFeature.RESTRICTS_KEYS, 158 MapFeature.RESTRICTS_VALUES, 159 CollectionFeature.SUPPORTS_ITERATOR_REMOVE, 160 CollectionFeature.SERIALIZABLE, 161 CollectionSize.ANY) 162 .suppressing(suppressForCheckedMap()) 163 .createTestSuite(); 164 } 165 166 public Test testsForCheckedSortedMap() { 167 return SortedMapTestSuiteBuilder.using( 168 new TestStringSortedMapGenerator() { 169 @Override 170 protected SortedMap<String, String> create(Entry<String, String>[] entries) { 171 SortedMap<String, String> map = populate(new TreeMap<String, String>(), entries); 172 return Collections.checkedSortedMap(map, String.class, String.class); 173 } 174 }) 175 .named("checkedSortedMap/TreeMap, natural") 176 .withFeatures( 177 MapFeature.GENERAL_PURPOSE, 178 MapFeature.ALLOWS_NULL_VALUES, 179 MapFeature.FAILS_FAST_ON_CONCURRENT_MODIFICATION, 180 MapFeature.RESTRICTS_KEYS, 181 MapFeature.RESTRICTS_VALUES, 182 CollectionFeature.KNOWN_ORDER, 183 CollectionFeature.SUPPORTS_ITERATOR_REMOVE, 184 CollectionFeature.SERIALIZABLE, 185 CollectionSize.ANY) 186 .suppressing(suppressForCheckedSortedMap()) 187 .createTestSuite(); 188 } 189 190 public Test testsForEmptyMap() { 191 return MapTestSuiteBuilder.using( 192 new TestStringMapGenerator() { 193 @Override 194 protected Map<String, String> create(Entry<String, String>[] entries) { 195 return emptyMap(); 196 } 197 }) 198 .named("emptyMap") 199 .withFeatures(CollectionFeature.SERIALIZABLE, CollectionSize.ZERO) 200 .suppressing(suppressForEmptyMap()) 201 .createTestSuite(); 202 } 203 204 public Test testsForSingletonMap() { 205 return MapTestSuiteBuilder.using( 206 new TestStringMapGenerator() { 207 @Override 208 protected Map<String, String> create(Entry<String, String>[] entries) { 209 return singletonMap(entries[0].getKey(), entries[0].getValue()); 210 } 211 }) 212 .named("singletonMap") 213 .withFeatures( 214 MapFeature.ALLOWS_NULL_KEYS, 215 MapFeature.ALLOWS_NULL_VALUES, 216 MapFeature.ALLOWS_ANY_NULL_QUERIES, 217 CollectionFeature.SERIALIZABLE, 218 CollectionSize.ONE) 219 .suppressing(suppressForSingletonMap()) 220 .createTestSuite(); 221 } 222 223 public Test testsForHashMap() { 224 return MapTestSuiteBuilder.using( 225 new TestStringMapGenerator() { 226 @Override 227 protected Map<String, String> create(Entry<String, String>[] entries) { 228 return toHashMap(entries); 229 } 230 }) 231 .named("HashMap") 232 .withFeatures( 233 MapFeature.GENERAL_PURPOSE, 234 MapFeature.ALLOWS_NULL_KEYS, 235 MapFeature.ALLOWS_NULL_VALUES, 236 MapFeature.ALLOWS_ANY_NULL_QUERIES, 237 MapFeature.FAILS_FAST_ON_CONCURRENT_MODIFICATION, 238 CollectionFeature.SUPPORTS_ITERATOR_REMOVE, 239 CollectionFeature.SERIALIZABLE, 240 CollectionSize.ANY) 241 .suppressing(suppressForHashMap()) 242 .createTestSuite(); 243 } 244 245 public Test testsForHashtable() { 246 return MapTestSuiteBuilder.using( 247 new TestStringMapGenerator() { 248 @Override 249 // We are testing Hashtable / testing our tests on Hashtable. 250 @SuppressWarnings("JdkObsolete") 251 protected Map<String, String> create(Entry<String, String>[] entries) { 252 return populate(new Hashtable<String, String>(), entries); 253 } 254 }) 255 .withFeatures( 256 MapFeature.GENERAL_PURPOSE, 257 MapFeature.FAILS_FAST_ON_CONCURRENT_MODIFICATION, 258 MapFeature.RESTRICTS_KEYS, 259 MapFeature.SUPPORTS_REMOVE, 260 CollectionFeature.FAILS_FAST_ON_CONCURRENT_MODIFICATION, 261 CollectionFeature.SERIALIZABLE, 262 CollectionFeature.SUPPORTS_ITERATOR_REMOVE, 263 CollectionFeature.SUPPORTS_REMOVE, 264 CollectionSize.ANY) 265 .named("Hashtable") 266 .suppressing(suppressForHashtable()) 267 .createTestSuite(); 268 } 269 270 public Test testsForLinkedHashMap() { 271 return MapTestSuiteBuilder.using( 272 new TestStringMapGenerator() { 273 @Override 274 protected Map<String, String> create(Entry<String, String>[] entries) { 275 return populate(new LinkedHashMap<String, String>(), entries); 276 } 277 }) 278 .named("LinkedHashMap") 279 .withFeatures( 280 MapFeature.GENERAL_PURPOSE, 281 MapFeature.ALLOWS_NULL_KEYS, 282 MapFeature.ALLOWS_NULL_VALUES, 283 MapFeature.ALLOWS_ANY_NULL_QUERIES, 284 MapFeature.FAILS_FAST_ON_CONCURRENT_MODIFICATION, 285 CollectionFeature.SUPPORTS_ITERATOR_REMOVE, 286 CollectionFeature.KNOWN_ORDER, 287 CollectionFeature.SERIALIZABLE, 288 CollectionSize.ANY) 289 .suppressing(suppressForLinkedHashMap()) 290 .createTestSuite(); 291 } 292 293 public Test testsForTreeMapNatural() { 294 return NavigableMapTestSuiteBuilder.using( 295 new TestStringSortedMapGenerator() { 296 @Override 297 protected SortedMap<String, String> create(Entry<String, String>[] entries) { 298 /* 299 * TODO(cpovirk): it would be nice to create an input Map and use 300 * the copy constructor here and in the other tests 301 */ 302 return populate(new TreeMap<String, String>(), entries); 303 } 304 }) 305 .named("TreeMap, natural") 306 .withFeatures( 307 MapFeature.GENERAL_PURPOSE, 308 MapFeature.ALLOWS_NULL_VALUES, 309 MapFeature.FAILS_FAST_ON_CONCURRENT_MODIFICATION, 310 CollectionFeature.SUPPORTS_ITERATOR_REMOVE, 311 CollectionFeature.KNOWN_ORDER, 312 CollectionFeature.SERIALIZABLE, 313 CollectionSize.ANY) 314 .suppressing(suppressForTreeMapNatural()) 315 .createTestSuite(); 316 } 317 318 public Test testsForTreeMapWithComparator() { 319 return NavigableMapTestSuiteBuilder.using( 320 new TestStringSortedMapGenerator() { 321 @Override 322 protected SortedMap<String, String> create(Entry<String, String>[] entries) { 323 return populate( 324 new TreeMap<String, String>(arbitraryNullFriendlyComparator()), entries); 325 } 326 }) 327 .named("TreeMap, with comparator") 328 .withFeatures( 329 MapFeature.GENERAL_PURPOSE, 330 MapFeature.ALLOWS_NULL_KEYS, 331 MapFeature.ALLOWS_NULL_VALUES, 332 MapFeature.ALLOWS_ANY_NULL_QUERIES, 333 MapFeature.FAILS_FAST_ON_CONCURRENT_MODIFICATION, 334 CollectionFeature.SUPPORTS_ITERATOR_REMOVE, 335 CollectionFeature.KNOWN_ORDER, 336 CollectionFeature.SERIALIZABLE, 337 CollectionSize.ANY) 338 .suppressing(suppressForTreeMapWithComparator()) 339 .createTestSuite(); 340 } 341 342 public Test testsForUnmodifiableMap() { 343 return MapTestSuiteBuilder.using( 344 new TestStringMapGenerator() { 345 @Override 346 protected Map<String, String> create(Entry<String, String>[] entries) { 347 return unmodifiableMap(toHashMap(entries)); 348 } 349 }) 350 .named("unmodifiableMap/HashMap") 351 .withFeatures( 352 MapFeature.ALLOWS_NULL_KEYS, 353 MapFeature.ALLOWS_NULL_VALUES, 354 MapFeature.ALLOWS_ANY_NULL_QUERIES, 355 CollectionFeature.SERIALIZABLE, 356 CollectionSize.ANY) 357 .suppressing(suppressForUnmodifiableMap()) 358 .createTestSuite(); 359 } 360 361 public Test testsForUnmodifiableSortedMap() { 362 return MapTestSuiteBuilder.using( 363 new TestStringSortedMapGenerator() { 364 @Override 365 protected SortedMap<String, String> create(Entry<String, String>[] entries) { 366 SortedMap<String, String> map = populate(new TreeMap<String, String>(), entries); 367 return Collections.unmodifiableSortedMap(map); 368 } 369 }) 370 .named("unmodifiableSortedMap/TreeMap, natural") 371 .withFeatures( 372 MapFeature.ALLOWS_NULL_VALUES, 373 CollectionFeature.KNOWN_ORDER, 374 CollectionFeature.SERIALIZABLE, 375 CollectionSize.ANY) 376 .suppressing(suppressForUnmodifiableSortedMap()) 377 .createTestSuite(); 378 } 379 380 public Test testsForEnumMap() { 381 return MapTestSuiteBuilder.using( 382 new TestEnumMapGenerator() { 383 @Override 384 protected Map<AnEnum, String> create(Entry<AnEnum, String>[] entries) { 385 return populate(new EnumMap<AnEnum, String>(AnEnum.class), entries); 386 } 387 }) 388 .named("EnumMap") 389 .withFeatures( 390 MapFeature.GENERAL_PURPOSE, 391 MapFeature.ALLOWS_NULL_VALUES, 392 MapFeature.RESTRICTS_KEYS, 393 CollectionFeature.SUPPORTS_ITERATOR_REMOVE, 394 CollectionFeature.KNOWN_ORDER, 395 CollectionFeature.SERIALIZABLE, 396 CollectionSize.ANY) 397 .suppressing(suppressForEnumMap()) 398 .createTestSuite(); 399 } 400 401 public Test testsForConcurrentHashMap() { 402 return ConcurrentMapTestSuiteBuilder.using( 403 new TestStringMapGenerator() { 404 @Override 405 protected Map<String, String> create(Entry<String, String>[] entries) { 406 return populate(new ConcurrentHashMap<String, String>(), entries); 407 } 408 }) 409 .named("ConcurrentHashMap") 410 .withFeatures( 411 MapFeature.GENERAL_PURPOSE, 412 CollectionFeature.SUPPORTS_ITERATOR_REMOVE, 413 CollectionFeature.SERIALIZABLE, 414 CollectionSize.ANY) 415 .suppressing(suppressForConcurrentHashMap()) 416 .createTestSuite(); 417 } 418 419 public Test testsForConcurrentSkipListMapNatural() { 420 return ConcurrentNavigableMapTestSuiteBuilder.using( 421 new TestStringSortedMapGenerator() { 422 @Override 423 protected SortedMap<String, String> create(Entry<String, String>[] entries) { 424 return populate(new ConcurrentSkipListMap<String, String>(), entries); 425 } 426 }) 427 .named("ConcurrentSkipListMap, natural") 428 .withFeatures( 429 MapFeature.GENERAL_PURPOSE, 430 CollectionFeature.SUPPORTS_ITERATOR_REMOVE, 431 CollectionFeature.KNOWN_ORDER, 432 CollectionFeature.SERIALIZABLE, 433 CollectionSize.ANY) 434 .suppressing(suppressForConcurrentSkipListMap()) 435 .createTestSuite(); 436 } 437 438 public Test testsForConcurrentSkipListMapWithComparator() { 439 return ConcurrentNavigableMapTestSuiteBuilder.using( 440 new TestStringSortedMapGenerator() { 441 @Override 442 protected SortedMap<String, String> create(Entry<String, String>[] entries) { 443 return populate( 444 new ConcurrentSkipListMap<String, String>(arbitraryNullFriendlyComparator()), 445 entries); 446 } 447 }) 448 .named("ConcurrentSkipListMap, with comparator") 449 .withFeatures( 450 MapFeature.GENERAL_PURPOSE, 451 CollectionFeature.SUPPORTS_ITERATOR_REMOVE, 452 CollectionFeature.KNOWN_ORDER, 453 CollectionFeature.SERIALIZABLE, 454 CollectionSize.ANY) 455 .suppressing(suppressForConcurrentSkipListMap()) 456 .createTestSuite(); 457 } 458 459 // TODO: IdentityHashMap, AbstractMap 460 461 private static Map<String, String> toHashMap(Entry<String, String>[] entries) { 462 return populate(new HashMap<String, String>(), entries); 463 } 464 465 // TODO: call conversion constructors or factory methods instead of using 466 // populate() on an empty map 467 @CanIgnoreReturnValue 468 private static <T, M extends Map<T, String>> M populate(M map, Entry<T, String>[] entries) { 469 for (Entry<T, String> entry : entries) { 470 map.put(entry.getKey(), entry.getValue()); 471 } 472 return map; 473 } 474 475 static <T> Comparator<T> arbitraryNullFriendlyComparator() { 476 return new NullFriendlyComparator<>(); 477 } 478 479 private static final class NullFriendlyComparator<T> implements Comparator<T>, Serializable { 480 @Override 481 public int compare(T left, T right) { 482 return String.valueOf(left).compareTo(String.valueOf(right)); 483 } 484 } 485}