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