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