MapTestSuiteBuilder.java revision 3ecfa412eddc4b084663f38d562537b86b9734d5
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 static com.google.common.collect.testing.DerivedCollectionGenerators.keySetGenerator;
20
21import com.google.common.collect.testing.DerivedCollectionGenerators.MapEntrySetGenerator;
22import com.google.common.collect.testing.DerivedCollectionGenerators.MapValueCollectionGenerator;
23import com.google.common.collect.testing.features.CollectionFeature;
24import com.google.common.collect.testing.features.CollectionSize;
25import com.google.common.collect.testing.features.Feature;
26import com.google.common.collect.testing.features.MapFeature;
27import com.google.common.collect.testing.testers.MapClearTester;
28import com.google.common.collect.testing.testers.MapContainsKeyTester;
29import com.google.common.collect.testing.testers.MapContainsValueTester;
30import com.google.common.collect.testing.testers.MapCreationTester;
31import com.google.common.collect.testing.testers.MapEntrySetTester;
32import com.google.common.collect.testing.testers.MapEqualsTester;
33import com.google.common.collect.testing.testers.MapGetTester;
34import com.google.common.collect.testing.testers.MapHashCodeTester;
35import com.google.common.collect.testing.testers.MapIsEmptyTester;
36import com.google.common.collect.testing.testers.MapPutAllTester;
37import com.google.common.collect.testing.testers.MapPutTester;
38import com.google.common.collect.testing.testers.MapRemoveTester;
39import com.google.common.collect.testing.testers.MapSerializationTester;
40import com.google.common.collect.testing.testers.MapSizeTester;
41import com.google.common.collect.testing.testers.MapToStringTester;
42import com.google.common.testing.SerializableTester;
43
44import junit.framework.TestSuite;
45
46import java.util.Arrays;
47import java.util.HashSet;
48import java.util.List;
49import java.util.Map;
50import java.util.Set;
51
52/**
53 * Creates, based on your criteria, a JUnit test suite that exhaustively tests
54 * a Map implementation.
55 *
56 * @author George van den Driessche
57 */
58public class MapTestSuiteBuilder<K, V>
59    extends PerCollectionSizeTestSuiteBuilder<
60        MapTestSuiteBuilder<K, V>,
61        TestMapGenerator<K, V>, Map<K, V>, Map.Entry<K, V>> {
62  public static <K, V> MapTestSuiteBuilder<K, V> using(
63      TestMapGenerator<K, V> generator) {
64    return new MapTestSuiteBuilder<K, V>().usingGenerator(generator);
65  }
66
67  @SuppressWarnings("unchecked") // Class parameters must be raw.
68  @Override protected List<Class<? extends AbstractTester>> getTesters() {
69    return Arrays.<Class<? extends AbstractTester>>asList(
70        MapClearTester.class,
71        MapContainsKeyTester.class,
72        MapContainsValueTester.class,
73        MapCreationTester.class,
74        MapEntrySetTester.class,
75        MapEqualsTester.class,
76        MapGetTester.class,
77        MapHashCodeTester.class,
78        MapIsEmptyTester.class,
79        MapPutTester.class,
80        MapPutAllTester.class,
81        MapRemoveTester.class,
82        MapSerializationTester.class,
83        MapSizeTester.class,
84        MapToStringTester.class
85    );
86  }
87
88  @Override
89  protected List<TestSuite> createDerivedSuites(
90      FeatureSpecificTestSuiteBuilder<
91          ?,
92          ? extends OneSizeTestContainerGenerator<Map<K, V>, Map.Entry<K, V>>>
93      parentBuilder) {
94    // TODO: Once invariant support is added, supply invariants to each of the
95    // derived suites, to check that mutations to the derived collections are
96    // reflected in the underlying map.
97
98    List<TestSuite> derivedSuites = super.createDerivedSuites(parentBuilder);
99
100    if (parentBuilder.getFeatures().contains(CollectionFeature.SERIALIZABLE)) {
101      derivedSuites.add(MapTestSuiteBuilder.using(
102              new ReserializedMapGenerator<K, V>(parentBuilder.getSubjectGenerator()))
103          .withFeatures(computeReserializedMapFeatures(parentBuilder.getFeatures()))
104          .named(parentBuilder.getName() + " reserialized")
105          .suppressing(parentBuilder.getSuppressedTests())
106          .createTestSuite());
107    }
108
109    derivedSuites.add(createDerivedEntrySetSuite(
110            new MapEntrySetGenerator<K, V>(parentBuilder.getSubjectGenerator()))
111        .withFeatures(computeEntrySetFeatures(parentBuilder.getFeatures()))
112        .named(parentBuilder.getName() + " entrySet")
113        .suppressing(parentBuilder.getSuppressedTests())
114        .createTestSuite());
115
116    derivedSuites.add(createDerivedKeySetSuite(
117            keySetGenerator(parentBuilder.getSubjectGenerator()))
118        .withFeatures(computeKeySetFeatures(parentBuilder.getFeatures()))
119        .named(parentBuilder.getName() + " keys")
120        .suppressing(parentBuilder.getSuppressedTests())
121        .createTestSuite());
122
123    derivedSuites.add(createDerivedValueCollectionSuite(
124            new MapValueCollectionGenerator<K, V>(
125                parentBuilder.getSubjectGenerator()))
126        .named(parentBuilder.getName() + " values")
127        .withFeatures(computeValuesCollectionFeatures(
128            parentBuilder.getFeatures()))
129        .suppressing(parentBuilder.getSuppressedTests())
130        .createTestSuite());
131
132    return derivedSuites;
133  }
134
135  protected SetTestSuiteBuilder<Map.Entry<K, V>> createDerivedEntrySetSuite(
136      TestSetGenerator<Map.Entry<K, V>> entrySetGenerator) {
137    return SetTestSuiteBuilder.using(entrySetGenerator);
138  }
139
140  protected SetTestSuiteBuilder<K> createDerivedKeySetSuite(TestSetGenerator<K> keySetGenerator) {
141    return SetTestSuiteBuilder.using(keySetGenerator);
142  }
143
144  protected CollectionTestSuiteBuilder<V> createDerivedValueCollectionSuite(
145      TestCollectionGenerator<V> valueCollectionGenerator) {
146    return CollectionTestSuiteBuilder.using(valueCollectionGenerator);
147  }
148
149  private static Set<Feature<?>> computeReserializedMapFeatures(
150      Set<Feature<?>> mapFeatures) {
151    Set<Feature<?>> derivedFeatures = Helpers.copyToSet(mapFeatures);
152    derivedFeatures.remove(CollectionFeature.SERIALIZABLE);
153    derivedFeatures.remove(CollectionFeature.SERIALIZABLE_INCLUDING_VIEWS);
154    return derivedFeatures;
155  }
156
157  private static Set<Feature<?>> computeEntrySetFeatures(
158      Set<Feature<?>> mapFeatures) {
159    Set<Feature<?>> entrySetFeatures =
160        computeCommonDerivedCollectionFeatures(mapFeatures);
161    if (mapFeatures.contains(MapFeature.ALLOWS_NULL_ENTRY_QUERIES)) {
162      entrySetFeatures.add(CollectionFeature.ALLOWS_NULL_QUERIES);
163    }
164    return entrySetFeatures;
165  }
166
167  private static Set<Feature<?>> computeKeySetFeatures(
168      Set<Feature<?>> mapFeatures) {
169    Set<Feature<?>> keySetFeatures =
170        computeCommonDerivedCollectionFeatures(mapFeatures);
171
172    // TODO(user): make this trigger only if the map is a submap
173    // currently, the KeySetGenerator won't work properly for a subset of a keyset of a submap
174    keySetFeatures.add(CollectionFeature.SUBSET_VIEW);
175    if (mapFeatures.contains(MapFeature.ALLOWS_NULL_KEYS)) {
176      keySetFeatures.add(CollectionFeature.ALLOWS_NULL_VALUES);
177    } else if (mapFeatures.contains(MapFeature.ALLOWS_NULL_KEY_QUERIES)) {
178      keySetFeatures.add(CollectionFeature.ALLOWS_NULL_QUERIES);
179    }
180
181    return keySetFeatures;
182  }
183
184  private static Set<Feature<?>> computeValuesCollectionFeatures(
185      Set<Feature<?>> mapFeatures) {
186    Set<Feature<?>> valuesCollectionFeatures =
187        computeCommonDerivedCollectionFeatures(mapFeatures);
188    if (mapFeatures.contains(MapFeature.ALLOWS_NULL_VALUE_QUERIES)) {
189      valuesCollectionFeatures.add(CollectionFeature.ALLOWS_NULL_QUERIES);
190    }
191    if (mapFeatures.contains(MapFeature.ALLOWS_NULL_VALUES)) {
192      valuesCollectionFeatures.add(CollectionFeature.ALLOWS_NULL_VALUES);
193    }
194
195    return valuesCollectionFeatures;
196  }
197
198  public static Set<Feature<?>> computeCommonDerivedCollectionFeatures(
199      Set<Feature<?>> mapFeatures) {
200    mapFeatures = new HashSet<Feature<?>>(mapFeatures);
201    Set<Feature<?>> derivedFeatures = new HashSet<Feature<?>>();
202    mapFeatures.remove(CollectionFeature.SERIALIZABLE);
203    if (mapFeatures.remove(CollectionFeature.SERIALIZABLE_INCLUDING_VIEWS)) {
204      derivedFeatures.add(CollectionFeature.SERIALIZABLE);
205    }
206    if (mapFeatures.contains(MapFeature.SUPPORTS_REMOVE)) {
207      derivedFeatures.add(CollectionFeature.SUPPORTS_REMOVE);
208    }
209    if (mapFeatures.contains(MapFeature.REJECTS_DUPLICATES_AT_CREATION)) {
210      derivedFeatures.add(CollectionFeature.REJECTS_DUPLICATES_AT_CREATION);
211    }
212    if (mapFeatures.contains(MapFeature.FAILS_FAST_ON_CONCURRENT_MODIFICATION)) {
213      derivedFeatures.add(CollectionFeature.FAILS_FAST_ON_CONCURRENT_MODIFICATION);
214    }
215    // add the intersection of CollectionFeature.values() and mapFeatures
216    for (CollectionFeature feature : CollectionFeature.values()) {
217      if (mapFeatures.contains(feature)) {
218        derivedFeatures.add(feature);
219      }
220    }
221    // add the intersection of CollectionSize.values() and mapFeatures
222    for (CollectionSize size : CollectionSize.values()) {
223      if (mapFeatures.contains(size)) {
224        derivedFeatures.add(size);
225      }
226    }
227    return derivedFeatures;
228  }
229
230  private static class ReserializedMapGenerator<K, V>
231      implements TestMapGenerator<K, V> {
232    private final OneSizeTestContainerGenerator<Map<K, V>, Map.Entry<K, V>>
233        mapGenerator;
234
235    public ReserializedMapGenerator(
236        OneSizeTestContainerGenerator<
237            Map<K, V>, Map.Entry<K, V>> mapGenerator) {
238      this.mapGenerator = mapGenerator;
239    }
240
241    @Override
242    public SampleElements<Map.Entry<K, V>> samples() {
243      return mapGenerator.samples();
244    }
245
246    @Override
247    public Map.Entry<K, V>[] createArray(int length) {
248      return mapGenerator.createArray(length);
249    }
250
251    @Override
252    public Iterable<Map.Entry<K, V>> order(
253        List<Map.Entry<K, V>> insertionOrder) {
254      return mapGenerator.order(insertionOrder);
255    }
256
257    @Override
258    public Map<K, V> create(Object... elements) {
259      return SerializableTester.reserialize(mapGenerator.create(elements));
260    }
261
262    @Override
263    public K[] createKeyArray(int length) {
264      return ((TestMapGenerator<K, V>) mapGenerator.getInnerGenerator())
265          .createKeyArray(length);
266    }
267
268    @Override
269    public V[] createValueArray(int length) {
270      return ((TestMapGenerator<K, V>) mapGenerator.getInnerGenerator())
271        .createValueArray(length);
272    }
273  }
274}
275