blob: 3eaedeae15aa983d2df3fb2537e0cfa682ba5f60 [file] [log] [blame]
/*
* Copyright (C) 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.common.collect.testing;
import com.google.common.collect.testing.features.CollectionFeature;
import com.google.common.collect.testing.features.CollectionSize;
import com.google.common.collect.testing.features.Feature;
import com.google.common.collect.testing.features.MapFeature;
import com.google.common.collect.testing.features.SetFeature;
import com.google.common.collect.testing.testers.MapClearTester;
import com.google.common.collect.testing.testers.MapContainsKeyTester;
import com.google.common.collect.testing.testers.MapContainsValueTester;
import com.google.common.collect.testing.testers.MapCreationTester;
import com.google.common.collect.testing.testers.MapEqualsTester;
import com.google.common.collect.testing.testers.MapGetTester;
import com.google.common.collect.testing.testers.MapHashCodeTester;
import com.google.common.collect.testing.testers.MapIsEmptyTester;
import com.google.common.collect.testing.testers.MapPutAllTester;
import com.google.common.collect.testing.testers.MapPutTester;
import com.google.common.collect.testing.testers.MapRemoveTester;
import com.google.common.collect.testing.testers.MapSizeTester;
import junit.framework.TestSuite;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
/**
* Creates, based on your criteria, a JUnit test suite that exhaustively tests
* a Map implementation.
*
* @author George van den Driessche
*/
public class MapTestSuiteBuilder<K, V>
extends PerCollectionSizeTestSuiteBuilder<
MapTestSuiteBuilder<K, V>,
TestMapGenerator<K, V>, Map<K, V>, Map.Entry<K, V>> {
public static <K, V> MapTestSuiteBuilder<K, V> using(
TestMapGenerator<K, V> generator) {
return new MapTestSuiteBuilder<K, V>().usingGenerator(generator);
}
@SuppressWarnings("unchecked")
@Override protected List<Class<? extends AbstractTester>> getTesters() {
return Arrays.<Class<? extends AbstractTester>>asList(
MapClearTester.class,
MapContainsKeyTester.class,
MapContainsValueTester.class,
MapCreationTester.class,
MapEqualsTester.class,
MapGetTester.class,
MapHashCodeTester.class,
MapIsEmptyTester.class,
MapPutTester.class,
MapPutAllTester.class,
MapRemoveTester.class,
MapSizeTester.class
);
}
@Override List<TestSuite> createDerivedSuites(
FeatureSpecificTestSuiteBuilder<
?,
? extends OneSizeTestContainerGenerator<Map<K, V>, Map.Entry<K, V>>>
parentBuilder) {
// TODO(George van den Driessche): Once invariant support is added, supply
// invariants to each of the derived suites, to check that mutations to
// the derived collections are reflected in the underlying map.
List<TestSuite> derivedSuites = super.createDerivedSuites(parentBuilder);
derivedSuites.add(SetTestSuiteBuilder.using(
new MapEntrySetGenerator<K, V>(parentBuilder.getSubjectGenerator()))
.withFeatures(computeEntrySetFeatures(parentBuilder.getFeatures()))
.named(parentBuilder.getName() + " entrySet")
.suppressing(parentBuilder.getSuppressedTests())
.createTestSuite());
derivedSuites.add(SetTestSuiteBuilder.using(
new MapKeySetGenerator<K, V>(parentBuilder.getSubjectGenerator()))
.withFeatures(computeKeySetFeatures(parentBuilder.getFeatures()))
.named(parentBuilder.getName() + " keys")
.suppressing(parentBuilder.getSuppressedTests())
.createTestSuite());
derivedSuites.add(CollectionTestSuiteBuilder.using(
new MapValueCollectionGenerator<K, V>(
parentBuilder.getSubjectGenerator()))
.named(parentBuilder.getName() + " values")
.withFeatures(computeValuesCollectionFeatures(
parentBuilder.getFeatures()))
.suppressing(parentBuilder.getSuppressedTests())
.createTestSuite());
return derivedSuites;
}
private static Set<Feature<?>> computeEntrySetFeatures(
Set<Feature<?>> mapFeatures) {
return computeCommonDerivedCollectionFeatures(mapFeatures);
}
private static Set<Feature<?>> computeKeySetFeatures(
Set<Feature<?>> mapFeatures) {
Set<Feature<?>> keySetFeatures =
computeCommonDerivedCollectionFeatures(mapFeatures);
if (mapFeatures.contains(MapFeature.ALLOWS_NULL_KEYS)) {
keySetFeatures.add(CollectionFeature.ALLOWS_NULL_VALUES);
}
return keySetFeatures;
}
private static Set<Feature<?>> computeValuesCollectionFeatures(
Set<Feature<?>> mapFeatures) {
Set<Feature<?>> valuesCollectionFeatures =
computeCommonDerivedCollectionFeatures(mapFeatures);
if (mapFeatures.contains(MapFeature.ALLOWS_NULL_VALUES)) {
valuesCollectionFeatures.add(CollectionFeature.ALLOWS_NULL_VALUES);
}
return valuesCollectionFeatures;
}
private static Set<Feature<?>> computeCommonDerivedCollectionFeatures(
Set<Feature<?>> mapFeatures) {
Set<Feature<?>> derivedFeatures = new HashSet<Feature<?>>();
if (mapFeatures.contains(MapFeature.SUPPORTS_REMOVE)) {
derivedFeatures.add(CollectionFeature.SUPPORTS_REMOVE);
derivedFeatures.add(CollectionFeature.SUPPORTS_REMOVE_ALL);
derivedFeatures.add(CollectionFeature.SUPPORTS_RETAIN_ALL);
}
if (mapFeatures.contains(MapFeature.SUPPORTS_CLEAR)) {
derivedFeatures.add(CollectionFeature.SUPPORTS_CLEAR);
}
if (mapFeatures.contains(MapFeature.REJECTS_DUPLICATES_AT_CREATION)) {
derivedFeatures.add(SetFeature.REJECTS_DUPLICATES_AT_CREATION);
}
// add the intersection of CollectionSize.values() and mapFeatures
for (CollectionSize size : CollectionSize.values()) {
if (mapFeatures.contains(size)) {
derivedFeatures.add(size);
}
}
return derivedFeatures;
}
private static class MapEntrySetGenerator<K, V>
implements TestSetGenerator<Map.Entry<K, V>> {
private final OneSizeTestContainerGenerator<Map<K, V>, Map.Entry<K, V>>
mapGenerator;
public MapEntrySetGenerator(
OneSizeTestContainerGenerator<
Map<K, V>, Map.Entry<K, V>> mapGenerator) {
this.mapGenerator = mapGenerator;
}
public SampleElements<Map.Entry<K, V>> samples() {
return mapGenerator.samples();
}
public Set<Map.Entry<K, V>> create(Object... elements) {
return mapGenerator.create(elements).entrySet();
}
public Map.Entry<K, V>[] createArray(int length) {
return mapGenerator.createArray(length);
}
public Iterable<Map.Entry<K, V>> order(
List<Map.Entry<K, V>> insertionOrder) {
return mapGenerator.order(insertionOrder);
}
}
// TODO(George van den Driessche): investigate some API changes to
// SampleElements that would tidy up parts of the following classes.
private static class MapKeySetGenerator<K, V> implements TestSetGenerator<K> {
private final OneSizeTestContainerGenerator<Map<K, V>, Map.Entry<K, V>>
mapGenerator;
private final SampleElements<K> samples;
public MapKeySetGenerator(
OneSizeTestContainerGenerator<Map<K, V>, Map.Entry<K, V>>
mapGenerator) {
this.mapGenerator = mapGenerator;
final SampleElements<Map.Entry<K, V>> mapSamples =
this.mapGenerator.samples();
this.samples = new SampleElements<K>(
mapSamples.e0.getKey(),
mapSamples.e1.getKey(),
mapSamples.e2.getKey(),
mapSamples.e3.getKey(),
mapSamples.e4.getKey());
}
public SampleElements<K> samples() {
return samples;
}
public Set<K> create(Object... elements) {
@SuppressWarnings("unchecked")
K[] keysArray = (K[]) elements;
// Start with a suitably shaped collection of entries
Collection<Map.Entry<K, V>> originalEntries =
mapGenerator.getSampleElements(elements.length);
// Create a copy of that, with the desired value for each key
Collection<Map.Entry<K, V>> entries =
new ArrayList<Entry<K, V>>(elements.length);
int i = 0;
for (Map.Entry<K, V> entry : originalEntries) {
entries.add(Helpers.mapEntry(keysArray[i++], entry.getValue()));
}
return mapGenerator.create(entries.toArray()).keySet();
}
public K[] createArray(int length) {
// TODO(George): with appropriate refactoring of OneSizeGenerator, we
// can perhaps tidy this up and get rid of the casts here and in
// MapValueCollectionGenerator.
//noinspection UnnecessaryLocalVariable
@SuppressWarnings("unchecked")
final K[] ks = ((TestMapGenerator<K, V>) mapGenerator.getInnerGenerator())
.createKeyArray(length);
return ks;
}
public Iterable<K> order(List<K> insertionOrder) {
return insertionOrder;
}
}
private static class MapValueCollectionGenerator<K, V>
implements TestCollectionGenerator<V> {
private final OneSizeTestContainerGenerator<Map<K, V>, Map.Entry<K, V>>
mapGenerator;
private final SampleElements<V> samples;
public MapValueCollectionGenerator(
OneSizeTestContainerGenerator<
Map<K, V>, Map.Entry<K, V>> mapGenerator) {
this.mapGenerator = mapGenerator;
final SampleElements<Map.Entry<K, V>> mapSamples =
this.mapGenerator.samples();
this.samples = new SampleElements<V>(
mapSamples.e0.getValue(),
mapSamples.e1.getValue(),
mapSamples.e2.getValue(),
mapSamples.e3.getValue(),
mapSamples.e4.getValue());
}
public SampleElements<V> samples() {
return samples;
}
public Collection<V> create(Object... elements) {
@SuppressWarnings("unchecked")
V[] valuesArray = (V[]) elements;
// Start with a suitably shaped collection of entries
Collection<Map.Entry<K, V>> originalEntries =
mapGenerator.getSampleElements(elements.length);
// Create a copy of that, with the desired value for each value
Collection<Map.Entry<K, V>> entries =
new ArrayList<Entry<K, V>>(elements.length);
int i = 0;
for (Map.Entry<K, V> entry : originalEntries) {
entries.add(Helpers.mapEntry(entry.getKey(), valuesArray[i++]));
}
return mapGenerator.create(entries.toArray()).values();
}
public V[] createArray(int length) {
//noinspection UnnecessaryLocalVariable
@SuppressWarnings("unchecked")
final V[] vs = ((TestMapGenerator<K, V>) mapGenerator.getInnerGenerator())
.createValueArray(length);
return vs;
}
public Iterable<V> order(List<V> insertionOrder) {
return insertionOrder;
}
}
}