1/*
2 * Copyright (C) 2009 The Guava Authors
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.google.common.collect.testing;
18
19import com.google.common.collect.testing.features.CollectionFeature;
20import com.google.common.collect.testing.features.CollectionSize;
21import com.google.common.collect.testing.features.MapFeature;
22
23import junit.framework.Test;
24import junit.framework.TestSuite;
25
26import java.lang.reflect.Method;
27import java.util.Collection;
28import java.util.Collections;
29import java.util.Comparator;
30import java.util.EnumMap;
31import java.util.HashMap;
32import java.util.LinkedHashMap;
33import java.util.List;
34import java.util.Map;
35import java.util.Map.Entry;
36import java.util.TreeMap;
37import java.util.concurrent.ConcurrentHashMap;
38
39/**
40 * Generates a test suite covering the {@link Map} implementations in the
41 * {@link java.util} package. Can be subclassed to specify tests that should
42 * be suppressed.
43 *
44 * @author Kevin Bourrillion
45 */
46public class TestsForMapsInJavaUtil {
47  public static Test suite() {
48    return new TestsForMapsInJavaUtil().allTests();
49  }
50
51  public Test allTests() {
52    TestSuite suite = new TestSuite("java.util Maps");
53    suite.addTest(testsForEmptyMap());
54    suite.addTest(testsForSingletonMap());
55    suite.addTest(testsForHashMap());
56    suite.addTest(testsForLinkedHashMap());
57    suite.addTest(testsForTreeMap());
58    suite.addTest(testsForEnumMap());
59    suite.addTest(testsForConcurrentHashMap());
60    return suite;
61  }
62
63  protected Collection<Method> suppressForEmptyMap() {
64    return Collections.emptySet();
65  }
66  protected Collection<Method> suppressForSingletonMap() {
67    return Collections.emptySet();
68  }
69  protected Collection<Method> suppressForHashMap() {
70    return Collections.emptySet();
71  }
72  protected Collection<Method> suppressForLinkedHashMap() {
73    return Collections.emptySet();
74  }
75  protected Collection<Method> suppressForTreeMap() {
76    return Collections.emptySet();
77  }
78  protected Collection<Method> suppressForEnumMap() {
79    return Collections.emptySet();
80  }
81  protected Collection<Method> suppressForConcurrentHashMap() {
82    return Collections.emptySet();
83  }
84
85  public Test testsForEmptyMap() {
86    return MapTestSuiteBuilder
87        .using(new TestStringMapGenerator() {
88            @Override protected Map<String, String> create(
89                Entry<String, String>[] entries) {
90              return Collections.emptyMap();
91            }
92          })
93        .named("emptyMap")
94        .withFeatures(
95            CollectionFeature.NONE,
96            CollectionSize.ZERO)
97        .suppressing(suppressForEmptyMap())
98        .createTestSuite();
99  }
100
101  public Test testsForSingletonMap() {
102    return MapTestSuiteBuilder
103        .using(new TestStringMapGenerator() {
104            @Override protected Map<String, String> create(
105                Entry<String, String>[] entries) {
106              return Collections.singletonMap(
107                  entries[0].getKey(), entries[0].getValue());
108            }
109          })
110        .named("singletonMap")
111        .withFeatures(
112            MapFeature.ALLOWS_NULL_KEYS,
113            MapFeature.ALLOWS_NULL_VALUES,
114            CollectionSize.ONE)
115        .suppressing(suppressForSingletonMap())
116        .createTestSuite();
117  }
118
119  public Test testsForHashMap() {
120    return MapTestSuiteBuilder
121        .using(new TestStringMapGenerator() {
122            @Override protected Map<String, String> create(
123                Entry<String, String>[] entries) {
124              return toHashMap(entries);
125            }
126            @Override public Iterable<Entry<String, String>> order(
127                List<Entry<String, String>> insertionOrder) {
128              /*
129               * For convenience, make this test double as a test that no tester
130               * calls order() on a container without the KNOWN_ORDER feature.
131               */
132              throw new UnsupportedOperationException();
133            }
134          })
135        .named("HashMap")
136        .withFeatures(
137            MapFeature.GENERAL_PURPOSE,
138            MapFeature.ALLOWS_NULL_KEYS,
139            MapFeature.ALLOWS_NULL_VALUES,
140            CollectionSize.ANY)
141        .suppressing(suppressForHashMap())
142        .createTestSuite();
143  }
144
145  public Test testsForLinkedHashMap() {
146    return MapTestSuiteBuilder
147        .using(new TestStringMapGenerator() {
148            @Override protected Map<String, String> create(
149                Entry<String, String>[] entries) {
150              return populate(new LinkedHashMap<String, String>(), entries);
151            }
152          })
153        .named("LinkedHashMap")
154        .withFeatures(
155            MapFeature.GENERAL_PURPOSE,
156            MapFeature.ALLOWS_NULL_KEYS,
157            MapFeature.ALLOWS_NULL_VALUES,
158            CollectionFeature.KNOWN_ORDER,
159            CollectionSize.ANY)
160        .suppressing(suppressForLinkedHashMap())
161        .createTestSuite();
162  }
163
164  public Test testsForTreeMap() {
165    return NavigableMapTestSuiteBuilder
166        .using(new TestStringMapGenerator() {
167            @Override protected Map<String, String> create(
168                Entry<String, String>[] entries) {
169              return populate(new TreeMap<String, String>(
170                  arbitraryNullFriendlyComparator()), entries);
171            }
172          })
173        .named("TreeMap")
174        .withFeatures(
175            MapFeature.GENERAL_PURPOSE,
176            MapFeature.ALLOWS_NULL_KEYS,
177            MapFeature.ALLOWS_NULL_VALUES,
178            CollectionFeature.KNOWN_ORDER,
179            CollectionSize.ANY)
180        .suppressing(suppressForTreeMap())
181        .createTestSuite();
182  }
183
184  public Test testsForEnumMap() {
185    return MapTestSuiteBuilder
186        .using(new TestEnumMapGenerator() {
187            @Override protected Map<AnEnum, String> create(
188                Entry<AnEnum, String>[] entries) {
189              return populate(
190                  new EnumMap<AnEnum, String>(AnEnum.class), entries);
191            }
192          })
193        .named("EnumMap")
194        .withFeatures(
195            MapFeature.GENERAL_PURPOSE,
196            MapFeature.ALLOWS_NULL_VALUES,
197            MapFeature.RESTRICTS_KEYS,
198            CollectionFeature.KNOWN_ORDER,
199            CollectionSize.ANY)
200        .suppressing(suppressForEnumMap())
201        .createTestSuite();
202  }
203
204  public Test testsForConcurrentHashMap() {
205    return MapTestSuiteBuilder
206        .using(new TestStringMapGenerator() {
207          @Override protected Map<String, String> create(
208              Entry<String, String>[] entries) {
209            return populate(new ConcurrentHashMap<String, String>(), entries);
210          }
211        })
212        .named("ConcurrentHashMap")
213        .withFeatures(
214            MapFeature.GENERAL_PURPOSE,
215            CollectionSize.ANY)
216        .suppressing(suppressForConcurrentHashMap())
217        .createTestSuite();
218  }
219
220  // TODO: IdentityHashMap, AbstractMap
221
222  private static Map<String, String> toHashMap(
223      Entry<String, String>[] entries) {
224    return populate(new HashMap<String, String>(), entries);
225  }
226
227  // TODO: call conversion constructors or factory methods instead of using
228  // populate() on an empty map
229  private static <T> Map<T, String> populate(
230      Map<T, String> map, Entry<T, String>[] entries) {
231    for (Entry<T, String> entry : entries) {
232      map.put(entry.getKey(), entry.getValue());
233    }
234    return map;
235  }
236
237  static <T> Comparator<T> arbitraryNullFriendlyComparator() {
238    return new Comparator<T>() {
239      @Override
240      public int compare(T left, T right) {
241        return String.valueOf(left).compareTo(String.valueOf(right));
242      }
243    };
244  }
245}
246