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}