001/*
002 * Copyright (C) 2012 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.google;
018
019import com.google.common.annotations.GwtIncompatible;
020import com.google.common.collect.BiMap;
021import com.google.common.collect.testing.AbstractTester;
022import com.google.common.collect.testing.FeatureSpecificTestSuiteBuilder;
023import com.google.common.collect.testing.MapTestSuiteBuilder;
024import com.google.common.collect.testing.OneSizeTestContainerGenerator;
025import com.google.common.collect.testing.PerCollectionSizeTestSuiteBuilder;
026import com.google.common.collect.testing.SetTestSuiteBuilder;
027import com.google.common.collect.testing.features.CollectionFeature;
028import com.google.common.collect.testing.features.Feature;
029import com.google.common.collect.testing.features.MapFeature;
030import com.google.common.collect.testing.google.DerivedGoogleCollectionGenerators.BiMapValueSetGenerator;
031import com.google.common.collect.testing.google.DerivedGoogleCollectionGenerators.InverseBiMapGenerator;
032import com.google.common.collect.testing.google.DerivedGoogleCollectionGenerators.MapGenerator;
033import com.google.common.collect.testing.testers.SetCreationTester;
034import java.util.ArrayList;
035import java.util.Collections;
036import java.util.HashSet;
037import java.util.List;
038import java.util.Map.Entry;
039import java.util.Set;
040import junit.framework.TestSuite;
041
042/**
043 * Creates, based on your criteria, a JUnit test suite that exhaustively tests a {@code BiMap}
044 * implementation.
045 *
046 * @author Louis Wasserman
047 */
048@GwtIncompatible
049public class BiMapTestSuiteBuilder<K, V>
050    extends PerCollectionSizeTestSuiteBuilder<
051        BiMapTestSuiteBuilder<K, V>, TestBiMapGenerator<K, V>, BiMap<K, V>, Entry<K, V>> {
052  public static <K, V> BiMapTestSuiteBuilder<K, V> using(TestBiMapGenerator<K, V> generator) {
053    return new BiMapTestSuiteBuilder<K, V>().usingGenerator(generator);
054  }
055
056  @SuppressWarnings("rawtypes") // class literals
057  @Override
058  protected List<Class<? extends AbstractTester>> getTesters() {
059    List<Class<? extends AbstractTester>> testers = new ArrayList<>();
060    testers.add(BiMapEntrySetTester.class);
061    testers.add(BiMapPutTester.class);
062    testers.add(BiMapInverseTester.class);
063    testers.add(BiMapRemoveTester.class);
064    testers.add(BiMapClearTester.class);
065    return testers;
066  }
067
068  enum NoRecurse implements Feature<Void> {
069    INVERSE;
070
071    @Override
072    public Set<Feature<? super Void>> getImpliedFeatures() {
073      return Collections.emptySet();
074    }
075  }
076
077  @Override
078  protected List<TestSuite> createDerivedSuites(
079      FeatureSpecificTestSuiteBuilder<
080              ?, ? extends OneSizeTestContainerGenerator<BiMap<K, V>, Entry<K, V>>>
081          parentBuilder) {
082    List<TestSuite> derived = super.createDerivedSuites(parentBuilder);
083    // TODO(cpovirk): consider using this approach (derived suites instead of extension) in
084    // ListTestSuiteBuilder, etc.?
085    derived.add(
086        MapTestSuiteBuilder.using(new MapGenerator<K, V>(parentBuilder.getSubjectGenerator()))
087            .withFeatures(parentBuilder.getFeatures())
088            .named(parentBuilder.getName() + " [Map]")
089            .suppressing(parentBuilder.getSuppressedTests())
090            .suppressing(SetCreationTester.class.getMethods())
091            // BiMap.entrySet() duplicate-handling behavior is too confusing for SetCreationTester
092            .withSetUp(parentBuilder.getSetUp())
093            .withTearDown(parentBuilder.getTearDown())
094            .createTestSuite());
095    /*
096     * TODO(cpovirk): the Map tests duplicate most of this effort by using a
097     * CollectionTestSuiteBuilder on values(). It would be nice to avoid that
098     */
099    derived.add(
100        SetTestSuiteBuilder.using(
101                new BiMapValueSetGenerator<K, V>(parentBuilder.getSubjectGenerator()))
102            .withFeatures(computeValuesSetFeatures(parentBuilder.getFeatures()))
103            .named(parentBuilder.getName() + " values [Set]")
104            .suppressing(parentBuilder.getSuppressedTests())
105            .suppressing(SetCreationTester.class.getMethods())
106            // BiMap.values() duplicate-handling behavior is too confusing for SetCreationTester
107            .withSetUp(parentBuilder.getSetUp())
108            .withTearDown(parentBuilder.getTearDown())
109            .createTestSuite());
110    if (!parentBuilder.getFeatures().contains(NoRecurse.INVERSE)) {
111      derived.add(
112          BiMapTestSuiteBuilder.using(
113                  new InverseBiMapGenerator<K, V>(parentBuilder.getSubjectGenerator()))
114              .withFeatures(computeInverseFeatures(parentBuilder.getFeatures()))
115              .named(parentBuilder.getName() + " inverse")
116              .suppressing(parentBuilder.getSuppressedTests())
117              .withSetUp(parentBuilder.getSetUp())
118              .withTearDown(parentBuilder.getTearDown())
119              .createTestSuite());
120    }
121
122    return derived;
123  }
124
125  private static Set<Feature<?>> computeInverseFeatures(Set<Feature<?>> mapFeatures) {
126    Set<Feature<?>> inverseFeatures = new HashSet<>(mapFeatures);
127
128    boolean nullKeys = inverseFeatures.remove(MapFeature.ALLOWS_NULL_KEYS);
129    boolean nullValues = inverseFeatures.remove(MapFeature.ALLOWS_NULL_VALUES);
130
131    if (nullKeys) {
132      inverseFeatures.add(MapFeature.ALLOWS_NULL_VALUES);
133    }
134    if (nullValues) {
135      inverseFeatures.add(MapFeature.ALLOWS_NULL_KEYS);
136    }
137
138    inverseFeatures.add(NoRecurse.INVERSE);
139    inverseFeatures.remove(CollectionFeature.KNOWN_ORDER);
140    inverseFeatures.add(MapFeature.REJECTS_DUPLICATES_AT_CREATION);
141
142    return inverseFeatures;
143  }
144
145  // TODO(lowasser): can we eliminate the duplication from MapTestSuiteBuilder here?
146
147  private static Set<Feature<?>> computeValuesSetFeatures(Set<Feature<?>> mapFeatures) {
148    Set<Feature<?>> valuesCollectionFeatures = computeCommonDerivedCollectionFeatures(mapFeatures);
149    valuesCollectionFeatures.add(CollectionFeature.ALLOWS_NULL_QUERIES);
150
151    if (mapFeatures.contains(MapFeature.ALLOWS_NULL_VALUES)) {
152      valuesCollectionFeatures.add(CollectionFeature.ALLOWS_NULL_VALUES);
153    }
154
155    valuesCollectionFeatures.add(CollectionFeature.REJECTS_DUPLICATES_AT_CREATION);
156
157    return valuesCollectionFeatures;
158  }
159
160  private static Set<Feature<?>> computeCommonDerivedCollectionFeatures(
161      Set<Feature<?>> mapFeatures) {
162    return MapTestSuiteBuilder.computeCommonDerivedCollectionFeatures(mapFeatures);
163  }
164}