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