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