BiMapTestSuiteBuilder.java revision 7dd252788645e940eada959bdde927426e2531c9
1/*
2 * Copyright (C) 2012 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.google;
18
19import com.google.common.collect.BiMap;
20import com.google.common.collect.testing.AbstractTester;
21import com.google.common.collect.testing.FeatureSpecificTestSuiteBuilder;
22import com.google.common.collect.testing.MapTestSuiteBuilder;
23import com.google.common.collect.testing.OneSizeTestContainerGenerator;
24import com.google.common.collect.testing.PerCollectionSizeTestSuiteBuilder;
25import com.google.common.collect.testing.SetTestSuiteBuilder;
26import com.google.common.collect.testing.features.CollectionFeature;
27import com.google.common.collect.testing.features.CollectionSize;
28import com.google.common.collect.testing.features.Feature;
29import com.google.common.collect.testing.features.MapFeature;
30import com.google.common.collect.testing.google.DerivedGoogleCollectionGenerators.BiMapValueSetGenerator;
31import com.google.common.collect.testing.google.DerivedGoogleCollectionGenerators.InverseBiMapGenerator;
32import com.google.common.collect.testing.google.DerivedGoogleCollectionGenerators.MapGenerator;
33import com.google.common.collect.testing.testers.SetCreationTester;
34
35import junit.framework.TestSuite;
36
37import java.util.ArrayList;
38import java.util.Collections;
39import java.util.HashSet;
40import java.util.List;
41import java.util.Map;
42import java.util.Map.Entry;
43import java.util.Set;
44
45/**
46 * Creates, based on your criteria, a JUnit test suite that exhaustively tests a {@code BiMap}
47 * implementation.
48 *
49 * @author Louis Wasserman
50 */
51public class BiMapTestSuiteBuilder<K, V>
52    extends PerCollectionSizeTestSuiteBuilder<BiMapTestSuiteBuilder<K, V>,
53            TestBiMapGenerator<K, V>, BiMap<K, V>, Map.Entry<K, V>> {
54  public static <K, V> BiMapTestSuiteBuilder<K, V> using(TestBiMapGenerator<K, V> generator) {
55    return new BiMapTestSuiteBuilder<K, V>().usingGenerator(generator);
56  }
57
58  @Override
59  protected List<Class<? extends AbstractTester>> getTesters() {
60    List<Class<? extends AbstractTester>> testers =
61        new ArrayList<Class<? extends AbstractTester>>();
62    testers.add(BiMapPutTester.class);
63    testers.add(BiMapInverseTester.class);
64    testers.add(BiMapRemoveTester.class);
65    testers.add(BiMapClearTester.class);
66    return testers;
67  }
68
69  enum NoRecurse implements Feature<Void> {
70    INVERSE;
71
72    @Override
73    public Set<Feature<? super Void>> getImpliedFeatures() {
74      return Collections.emptySet();
75    }
76  }
77
78  @Override
79  protected
80      List<TestSuite>
81      createDerivedSuites(
82          FeatureSpecificTestSuiteBuilder<?,
83              ? extends OneSizeTestContainerGenerator<BiMap<K, V>, Entry<K, V>>> parentBuilder) {
84    List<TestSuite> derived = super.createDerivedSuites(parentBuilder);
85    // TODO(cpovirk): consider using this approach (derived suites instead of extension) in
86    // ListTestSuiteBuilder, etc.?
87    derived.add(MapTestSuiteBuilder
88        .using(new MapGenerator<K, V>(parentBuilder.getSubjectGenerator()))
89        .withFeatures(parentBuilder.getFeatures())
90        .named(parentBuilder.getName() + " [Map]")
91        .suppressing(parentBuilder.getSuppressedTests())
92        .suppressing(SetCreationTester.class.getMethods())
93           // BiMap.entrySet() duplicate-handling behavior is too confusing for SetCreationTester
94        .createTestSuite());
95    /*
96     * TODO(cpovirk): the Map tests duplicate most of this effort by using a
97     * CollectionTestSuiteBuilder on values(). It would be nice to avoid that
98     */
99    derived.add(SetTestSuiteBuilder
100        .using(new BiMapValueSetGenerator<K, V>(parentBuilder.getSubjectGenerator()))
101        .withFeatures(computeValuesSetFeatures(parentBuilder.getFeatures()))
102        .named(parentBuilder.getName() + " values [Set]")
103        .suppressing(parentBuilder.getSuppressedTests())
104        .suppressing(SetCreationTester.class.getMethods())
105          // BiMap.values() duplicate-handling behavior is too confusing for SetCreationTester
106        .createTestSuite());
107    if (!parentBuilder.getFeatures().contains(NoRecurse.INVERSE)) {
108      derived.add(BiMapTestSuiteBuilder
109          .using(new InverseBiMapGenerator<K, V>(parentBuilder.getSubjectGenerator()))
110          .withFeatures(computeInverseFeatures(parentBuilder.getFeatures()))
111          .named(parentBuilder.getName() + " inverse")
112          .suppressing(parentBuilder.getSuppressedTests())
113          .createTestSuite());
114    }
115
116    return derived;
117  }
118
119  private static Set<Feature<?>> computeInverseFeatures(Set<Feature<?>> mapFeatures) {
120    Set<Feature<?>> inverseFeatures = new HashSet<Feature<?>>(mapFeatures);
121
122    boolean nullKeys = inverseFeatures.remove(MapFeature.ALLOWS_NULL_KEYS);
123    boolean nullValues = inverseFeatures.remove(MapFeature.ALLOWS_NULL_VALUES);
124
125    if (nullKeys) {
126      inverseFeatures.add(MapFeature.ALLOWS_NULL_VALUES);
127    }
128    if (nullValues) {
129      inverseFeatures.add(MapFeature.ALLOWS_NULL_KEYS);
130    }
131
132    inverseFeatures.add(NoRecurse.INVERSE);
133    inverseFeatures.remove(CollectionFeature.KNOWN_ORDER);
134    inverseFeatures.add(MapFeature.REJECTS_DUPLICATES_AT_CREATION);
135
136    return inverseFeatures;
137  }
138
139  // TODO(user): can we eliminate the duplication from MapTestSuiteBuilder here?
140
141  private static Set<Feature<?>> computeValuesSetFeatures(
142      Set<Feature<?>> mapFeatures) {
143    Set<Feature<?>> valuesCollectionFeatures =
144        computeCommonDerivedCollectionFeatures(mapFeatures);
145    valuesCollectionFeatures.add(CollectionFeature.ALLOWS_NULL_QUERIES);
146
147    if (mapFeatures.contains(MapFeature.ALLOWS_NULL_VALUES)) {
148      valuesCollectionFeatures.add(CollectionFeature.ALLOWS_NULL_VALUES);
149    }
150
151    valuesCollectionFeatures.add(CollectionFeature.REJECTS_DUPLICATES_AT_CREATION);
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    }
162    if (mapFeatures.contains(MapFeature.REJECTS_DUPLICATES_AT_CREATION)) {
163      derivedFeatures.add(CollectionFeature.REJECTS_DUPLICATES_AT_CREATION);
164    }
165    // add the intersection of CollectionSize.values() and mapFeatures
166    for (CollectionSize size : CollectionSize.values()) {
167      if (mapFeatures.contains(size)) {
168        derivedFeatures.add(size);
169      }
170    }
171    return derivedFeatures;
172  }
173}
174