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