ListMultimapTestSuiteBuilder.java revision dbd967a6e5c96cc1a97c5521f88dc1564ba2f81b
1/*
2 * Copyright (C) 2008 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.Feature;
22import com.google.common.collect.testing.features.MapFeature;
23import com.google.common.collect.testing.testers.MapClearTester;
24import com.google.common.collect.testing.testers.MapContainsKeyTester;
25import com.google.common.collect.testing.testers.MapContainsValueTester;
26import com.google.common.collect.testing.testers.MapCreationTester;
27import com.google.common.collect.testing.testers.MapEqualsTester;
28import com.google.common.collect.testing.testers.MapGetTester;
29import com.google.common.collect.testing.testers.MapHashCodeTester;
30import com.google.common.collect.testing.testers.MapIsEmptyTester;
31import com.google.common.collect.testing.testers.MapPutAllTester;
32import com.google.common.collect.testing.testers.MapPutTester;
33import com.google.common.collect.testing.testers.MapRemoveTester;
34import com.google.common.collect.testing.testers.MapSizeTester;
35
36import junit.framework.TestSuite;
37
38import java.util.ArrayList;
39import java.util.Arrays;
40import java.util.Collection;
41import java.util.HashSet;
42import java.util.List;
43import java.util.Map;
44import java.util.Map.Entry;
45import java.util.Set;
46
47/**
48 * Creates, based on your criteria, a JUnit test suite that exhaustively tests
49 * a Map implementation.
50 *
51 * @author George van den Driessche
52 */
53public class MapTestSuiteBuilder<K, V>
54    extends PerCollectionSizeTestSuiteBuilder<
55        MapTestSuiteBuilder<K, V>,
56        TestMapGenerator<K, V>, Map<K, V>, Map.Entry<K, V>> {
57  public static <K, V> MapTestSuiteBuilder<K, V> using(
58      TestMapGenerator<K, V> generator) {
59    return new MapTestSuiteBuilder<K, V>().usingGenerator(generator);
60  }
61
62  @SuppressWarnings("unchecked") // Class parameters must be raw.
63  @Override protected List<Class<? extends AbstractTester>> getTesters() {
64    return Arrays.<Class<? extends AbstractTester>>asList(
65        MapClearTester.class,
66        MapContainsKeyTester.class,
67        MapContainsValueTester.class,
68        MapCreationTester.class,
69        MapEqualsTester.class,
70        MapGetTester.class,
71        MapHashCodeTester.class,
72        MapIsEmptyTester.class,
73        MapPutTester.class,
74        MapPutAllTester.class,
75        MapRemoveTester.class,
76        MapSizeTester.class
77    );
78  }
79
80  @Override List<TestSuite> createDerivedSuites(
81      FeatureSpecificTestSuiteBuilder<
82          ?,
83          ? extends OneSizeTestContainerGenerator<Map<K, V>, Map.Entry<K, V>>>
84      parentBuilder) {
85    // TODO: Once invariant support is added, supply invariants to each of the
86    // derived suites, to check that mutations to the derived collections are
87    // reflected in the underlying map.
88
89    List<TestSuite> derivedSuites = super.createDerivedSuites(parentBuilder);
90
91    derivedSuites.add(SetTestSuiteBuilder.using(
92            new MapEntrySetGenerator<K, V>(parentBuilder.getSubjectGenerator()))
93        .withFeatures(computeEntrySetFeatures(parentBuilder.getFeatures()))
94        .named(parentBuilder.getName() + " entrySet")
95        .suppressing(parentBuilder.getSuppressedTests())
96        .createTestSuite());
97
98    derivedSuites.add(createDerivedKeySetSuite(
99            new MapKeySetGenerator<K, V>(parentBuilder.getSubjectGenerator()))
100        .withFeatures(computeKeySetFeatures(parentBuilder.getFeatures()))
101        .named(parentBuilder.getName() + " keys")
102        .suppressing(parentBuilder.getSuppressedTests())
103        .createTestSuite());
104
105    derivedSuites.add(CollectionTestSuiteBuilder.using(
106            new MapValueCollectionGenerator<K, V>(
107                parentBuilder.getSubjectGenerator()))
108        .named(parentBuilder.getName() + " values")
109        .withFeatures(computeValuesCollectionFeatures(
110            parentBuilder.getFeatures()))
111        .suppressing(parentBuilder.getSuppressedTests())
112        .createTestSuite());
113
114    return derivedSuites;
115  }
116
117  protected SetTestSuiteBuilder<K> createDerivedKeySetSuite(TestSetGenerator<K> keySetGenerator) {
118    return SetTestSuiteBuilder.using(keySetGenerator);
119  }
120
121  private static Set<Feature<?>> computeEntrySetFeatures(
122      Set<Feature<?>> mapFeatures) {
123    Set<Feature<?>> entrySetFeatures =
124        computeCommonDerivedCollectionFeatures(mapFeatures);
125    entrySetFeatures.add(CollectionFeature.ALLOWS_NULL_QUERIES);
126    return entrySetFeatures;
127  }
128
129  private static Set<Feature<?>> computeKeySetFeatures(
130      Set<Feature<?>> mapFeatures) {
131    Set<Feature<?>> keySetFeatures =
132        computeCommonDerivedCollectionFeatures(mapFeatures);
133
134    if (mapFeatures.contains(MapFeature.ALLOWS_NULL_KEYS)) {
135      keySetFeatures.add(CollectionFeature.ALLOWS_NULL_VALUES);
136    } else if (mapFeatures.contains(MapFeature.ALLOWS_NULL_QUERIES)) {
137      keySetFeatures.add(CollectionFeature.ALLOWS_NULL_QUERIES);
138    }
139
140    return keySetFeatures;
141  }
142
143  private static Set<Feature<?>> computeValuesCollectionFeatures(
144      Set<Feature<?>> mapFeatures) {
145    Set<Feature<?>> valuesCollectionFeatures =
146        computeCommonDerivedCollectionFeatures(mapFeatures);
147    valuesCollectionFeatures.add(CollectionFeature.ALLOWS_NULL_QUERIES);
148
149    if (mapFeatures.contains(MapFeature.ALLOWS_NULL_VALUES)) {
150      valuesCollectionFeatures.add(CollectionFeature.ALLOWS_NULL_VALUES);
151    }
152
153    return valuesCollectionFeatures;
154  }
155
156  private static Set<Feature<?>> computeCommonDerivedCollectionFeatures(
157      Set<Feature<?>> mapFeatures) {
158    Set<Feature<?>> derivedFeatures = new HashSet<Feature<?>>();
159    if (mapFeatures.contains(MapFeature.SUPPORTS_REMOVE)) {
160      derivedFeatures.add(CollectionFeature.SUPPORTS_REMOVE);
161      derivedFeatures.add(CollectionFeature.SUPPORTS_REMOVE_ALL);
162      derivedFeatures.add(CollectionFeature.SUPPORTS_RETAIN_ALL);
163    }
164    if (mapFeatures.contains(MapFeature.SUPPORTS_CLEAR)) {
165      derivedFeatures.add(CollectionFeature.SUPPORTS_CLEAR);
166    }
167    if (mapFeatures.contains(MapFeature.REJECTS_DUPLICATES_AT_CREATION)) {
168      derivedFeatures.add(CollectionFeature.REJECTS_DUPLICATES_AT_CREATION);
169    }
170    // add the intersection of CollectionSize.values() and mapFeatures
171    for (CollectionSize size : CollectionSize.values()) {
172      if (mapFeatures.contains(size)) {
173        derivedFeatures.add(size);
174      }
175    }
176    return derivedFeatures;
177  }
178
179  private static class MapEntrySetGenerator<K, V>
180      implements TestSetGenerator<Map.Entry<K, V>> {
181    private final OneSizeTestContainerGenerator<Map<K, V>, Map.Entry<K, V>>
182        mapGenerator;
183
184    public MapEntrySetGenerator(
185        OneSizeTestContainerGenerator<
186            Map<K, V>, Map.Entry<K, V>> mapGenerator) {
187      this.mapGenerator = mapGenerator;
188    }
189
190    @Override
191    public SampleElements<Map.Entry<K, V>> samples() {
192      return mapGenerator.samples();
193    }
194
195    @Override
196    public Set<Map.Entry<K, V>> create(Object... elements) {
197      return mapGenerator.create(elements).entrySet();
198    }
199
200    @Override
201    public Map.Entry<K, V>[] createArray(int length) {
202      return mapGenerator.createArray(length);
203    }
204
205    @Override
206    public Iterable<Map.Entry<K, V>> order(
207        List<Map.Entry<K, V>> insertionOrder) {
208      return mapGenerator.order(insertionOrder);
209    }
210  }
211
212  // TODO: investigate some API changes to SampleElements that would tidy up
213  // parts of the following classes.
214
215  private static class MapKeySetGenerator<K, V> implements TestSetGenerator<K> {
216    private final OneSizeTestContainerGenerator<Map<K, V>, Map.Entry<K, V>>
217        mapGenerator;
218    private final SampleElements<K> samples;
219
220    public MapKeySetGenerator(
221        OneSizeTestContainerGenerator<Map<K, V>, Map.Entry<K, V>>
222            mapGenerator) {
223      this.mapGenerator = mapGenerator;
224      final SampleElements<Map.Entry<K, V>> mapSamples =
225          this.mapGenerator.samples();
226      this.samples = new SampleElements<K>(
227          mapSamples.e0.getKey(),
228          mapSamples.e1.getKey(),
229          mapSamples.e2.getKey(),
230          mapSamples.e3.getKey(),
231          mapSamples.e4.getKey());
232    }
233
234    @Override
235    public SampleElements<K> samples() {
236      return samples;
237    }
238
239    @Override
240    public Set<K> create(Object... elements) {
241      @SuppressWarnings("unchecked")
242      K[] keysArray = (K[]) elements;
243
244      // Start with a suitably shaped collection of entries
245      Collection<Map.Entry<K, V>> originalEntries =
246          mapGenerator.getSampleElements(elements.length);
247
248      // Create a copy of that, with the desired value for each key
249      Collection<Map.Entry<K, V>> entries =
250          new ArrayList<Entry<K, V>>(elements.length);
251      int i = 0;
252      for (Map.Entry<K, V> entry : originalEntries) {
253        entries.add(Helpers.mapEntry(keysArray[i++], entry.getValue()));
254      }
255
256      return mapGenerator.create(entries.toArray()).keySet();
257    }
258
259    @Override
260    public K[] createArray(int length) {
261      // TODO: with appropriate refactoring of OneSizeGenerator, we can perhaps
262      // tidy this up and get rid of the casts here and in
263      // MapValueCollectionGenerator.
264
265      return ((TestMapGenerator<K, V>) mapGenerator.getInnerGenerator())
266          .createKeyArray(length);
267    }
268
269    @Override
270    public Iterable<K> order(List<K> insertionOrder) {
271      return insertionOrder;
272    }
273  }
274
275  private static class MapValueCollectionGenerator<K, V>
276      implements TestCollectionGenerator<V> {
277    private final OneSizeTestContainerGenerator<Map<K, V>, Map.Entry<K, V>>
278        mapGenerator;
279    private final SampleElements<V> samples;
280
281    public MapValueCollectionGenerator(
282        OneSizeTestContainerGenerator<
283            Map<K, V>, Map.Entry<K, V>> mapGenerator) {
284      this.mapGenerator = mapGenerator;
285      final SampleElements<Map.Entry<K, V>> mapSamples =
286          this.mapGenerator.samples();
287      this.samples = new SampleElements<V>(
288          mapSamples.e0.getValue(),
289          mapSamples.e1.getValue(),
290          mapSamples.e2.getValue(),
291          mapSamples.e3.getValue(),
292          mapSamples.e4.getValue());
293    }
294
295    @Override
296    public SampleElements<V> samples() {
297      return samples;
298    }
299
300    @Override
301    public Collection<V> create(Object... elements) {
302      @SuppressWarnings("unchecked")
303      V[] valuesArray = (V[]) elements;
304
305      // Start with a suitably shaped collection of entries
306      Collection<Map.Entry<K, V>> originalEntries =
307          mapGenerator.getSampleElements(elements.length);
308
309      // Create a copy of that, with the desired value for each value
310      Collection<Map.Entry<K, V>> entries =
311          new ArrayList<Entry<K, V>>(elements.length);
312      int i = 0;
313      for (Map.Entry<K, V> entry : originalEntries) {
314        entries.add(Helpers.mapEntry(entry.getKey(), valuesArray[i++]));
315      }
316
317      return mapGenerator.create(entries.toArray()).values();
318    }
319
320    @Override
321    public V[] createArray(int length) {
322      //noinspection UnnecessaryLocalVariable
323      final V[] vs = ((TestMapGenerator<K, V>) mapGenerator.getInnerGenerator())
324          .createValueArray(length);
325      return vs;
326    }
327
328    @Override
329    public Iterable<V> order(List<V> insertionOrder) {
330      return insertionOrder;
331    }
332  }
333}
334