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  @Override
057  protected List<Class<? extends AbstractTester>> getTesters() {
058    List<Class<? extends AbstractTester>> testers = new ArrayList<>();
059    testers.add(BiMapEntrySetTester.class);
060    testers.add(BiMapPutTester.class);
061    testers.add(BiMapInverseTester.class);
062    testers.add(BiMapRemoveTester.class);
063    testers.add(BiMapClearTester.class);
064    return testers;
065  }
066
067  enum NoRecurse implements Feature<Void> {
068    INVERSE;
069
070    @Override
071    public Set<Feature<? super Void>> getImpliedFeatures() {
072      return Collections.emptySet();
073    }
074  }
075
076  @Override
077  protected List<TestSuite> createDerivedSuites(
078      FeatureSpecificTestSuiteBuilder<
079              ?, ? extends OneSizeTestContainerGenerator<BiMap<K, V>, Entry<K, V>>>
080          parentBuilder) {
081    List<TestSuite> derived = super.createDerivedSuites(parentBuilder);
082    // TODO(cpovirk): consider using this approach (derived suites instead of extension) in
083    // ListTestSuiteBuilder, etc.?
084    derived.add(
085        MapTestSuiteBuilder.using(new MapGenerator<K, V>(parentBuilder.getSubjectGenerator()))
086            .withFeatures(parentBuilder.getFeatures())
087            .named(parentBuilder.getName() + " [Map]")
088            .suppressing(parentBuilder.getSuppressedTests())
089            .suppressing(SetCreationTester.class.getMethods())
090            // BiMap.entrySet() duplicate-handling behavior is too confusing for SetCreationTester
091            .createTestSuite());
092    /*
093     * TODO(cpovirk): the Map tests duplicate most of this effort by using a
094     * CollectionTestSuiteBuilder on values(). It would be nice to avoid that
095     */
096    derived.add(
097        SetTestSuiteBuilder.using(
098                new BiMapValueSetGenerator<K, V>(parentBuilder.getSubjectGenerator()))
099            .withFeatures(computeValuesSetFeatures(parentBuilder.getFeatures()))
100            .named(parentBuilder.getName() + " values [Set]")
101            .suppressing(parentBuilder.getSuppressedTests())
102            .suppressing(SetCreationTester.class.getMethods())
103            // BiMap.values() duplicate-handling behavior is too confusing for SetCreationTester
104            .createTestSuite());
105    if (!parentBuilder.getFeatures().contains(NoRecurse.INVERSE)) {
106      derived.add(
107          BiMapTestSuiteBuilder.using(
108                  new InverseBiMapGenerator<K, V>(parentBuilder.getSubjectGenerator()))
109              .withFeatures(computeInverseFeatures(parentBuilder.getFeatures()))
110              .named(parentBuilder.getName() + " inverse")
111              .suppressing(parentBuilder.getSuppressedTests())
112              .createTestSuite());
113    }
114
115    return derived;
116  }
117
118  private static Set<Feature<?>> computeInverseFeatures(Set<Feature<?>> mapFeatures) {
119    Set<Feature<?>> inverseFeatures = new HashSet<>(mapFeatures);
120
121    boolean nullKeys = inverseFeatures.remove(MapFeature.ALLOWS_NULL_KEYS);
122    boolean nullValues = inverseFeatures.remove(MapFeature.ALLOWS_NULL_VALUES);
123
124    if (nullKeys) {
125      inverseFeatures.add(MapFeature.ALLOWS_NULL_VALUES);
126    }
127    if (nullValues) {
128      inverseFeatures.add(MapFeature.ALLOWS_NULL_KEYS);
129    }
130
131    inverseFeatures.add(NoRecurse.INVERSE);
132    inverseFeatures.remove(CollectionFeature.KNOWN_ORDER);
133    inverseFeatures.add(MapFeature.REJECTS_DUPLICATES_AT_CREATION);
134
135    return inverseFeatures;
136  }
137
138  // TODO(lowasser): can we eliminate the duplication from MapTestSuiteBuilder here?
139
140  private static Set<Feature<?>> computeValuesSetFeatures(Set<Feature<?>> mapFeatures) {
141    Set<Feature<?>> valuesCollectionFeatures = computeCommonDerivedCollectionFeatures(mapFeatures);
142    valuesCollectionFeatures.add(CollectionFeature.ALLOWS_NULL_QUERIES);
143
144    if (mapFeatures.contains(MapFeature.ALLOWS_NULL_VALUES)) {
145      valuesCollectionFeatures.add(CollectionFeature.ALLOWS_NULL_VALUES);
146    }
147
148    valuesCollectionFeatures.add(CollectionFeature.REJECTS_DUPLICATES_AT_CREATION);
149
150    return valuesCollectionFeatures;
151  }
152
153  private static Set<Feature<?>> computeCommonDerivedCollectionFeatures(
154      Set<Feature<?>> mapFeatures) {
155    return MapTestSuiteBuilder.computeCommonDerivedCollectionFeatures(mapFeatures);
156  }
157}