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}