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}