/* * Copyright (C) 2008 The Guava Authors * * 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.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 extends PerCollectionSizeTestSuiteBuilder< MapTestSuiteBuilder, TestMapGenerator, Map, Map.Entry> { public static MapTestSuiteBuilder using( TestMapGenerator generator) { return new MapTestSuiteBuilder().usingGenerator(generator); } @SuppressWarnings("unchecked") // Class parameters must be raw. @Override protected List> getTesters() { return Arrays.>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 createDerivedSuites( FeatureSpecificTestSuiteBuilder< ?, ? extends OneSizeTestContainerGenerator, Map.Entry>> parentBuilder) { // TODO: 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 derivedSuites = super.createDerivedSuites(parentBuilder); derivedSuites.add(SetTestSuiteBuilder.using( new MapEntrySetGenerator(parentBuilder.getSubjectGenerator())) .withFeatures(computeEntrySetFeatures(parentBuilder.getFeatures())) .named(parentBuilder.getName() + " entrySet") .suppressing(parentBuilder.getSuppressedTests()) .createTestSuite()); derivedSuites.add(createDerivedKeySetSuite( new MapKeySetGenerator(parentBuilder.getSubjectGenerator())) .withFeatures(computeKeySetFeatures(parentBuilder.getFeatures())) .named(parentBuilder.getName() + " keys") .suppressing(parentBuilder.getSuppressedTests()) .createTestSuite()); derivedSuites.add(CollectionTestSuiteBuilder.using( new MapValueCollectionGenerator( parentBuilder.getSubjectGenerator())) .named(parentBuilder.getName() + " values") .withFeatures(computeValuesCollectionFeatures( parentBuilder.getFeatures())) .suppressing(parentBuilder.getSuppressedTests()) .createTestSuite()); return derivedSuites; } protected SetTestSuiteBuilder createDerivedKeySetSuite(TestSetGenerator keySetGenerator) { return SetTestSuiteBuilder.using(keySetGenerator); } private static Set> computeEntrySetFeatures( Set> mapFeatures) { Set> entrySetFeatures = computeCommonDerivedCollectionFeatures(mapFeatures); entrySetFeatures.add(CollectionFeature.ALLOWS_NULL_QUERIES); return entrySetFeatures; } private static Set> computeKeySetFeatures( Set> mapFeatures) { Set> keySetFeatures = computeCommonDerivedCollectionFeatures(mapFeatures); if (mapFeatures.contains(MapFeature.ALLOWS_NULL_KEYS)) { keySetFeatures.add(CollectionFeature.ALLOWS_NULL_VALUES); } else if (mapFeatures.contains(MapFeature.ALLOWS_NULL_QUERIES)) { keySetFeatures.add(CollectionFeature.ALLOWS_NULL_QUERIES); } return keySetFeatures; } private static Set> computeValuesCollectionFeatures( Set> mapFeatures) { Set> valuesCollectionFeatures = computeCommonDerivedCollectionFeatures(mapFeatures); valuesCollectionFeatures.add(CollectionFeature.ALLOWS_NULL_QUERIES); if (mapFeatures.contains(MapFeature.ALLOWS_NULL_VALUES)) { valuesCollectionFeatures.add(CollectionFeature.ALLOWS_NULL_VALUES); } return valuesCollectionFeatures; } private static Set> computeCommonDerivedCollectionFeatures( Set> mapFeatures) { Set> derivedFeatures = new HashSet>(); 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(CollectionFeature.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 implements TestSetGenerator> { private final OneSizeTestContainerGenerator, Map.Entry> mapGenerator; public MapEntrySetGenerator( OneSizeTestContainerGenerator< Map, Map.Entry> mapGenerator) { this.mapGenerator = mapGenerator; } @Override public SampleElements> samples() { return mapGenerator.samples(); } @Override public Set> create(Object... elements) { return mapGenerator.create(elements).entrySet(); } @Override public Map.Entry[] createArray(int length) { return mapGenerator.createArray(length); } @Override public Iterable> order( List> insertionOrder) { return mapGenerator.order(insertionOrder); } } // TODO: investigate some API changes to SampleElements that would tidy up // parts of the following classes. private static class MapKeySetGenerator implements TestSetGenerator { private final OneSizeTestContainerGenerator, Map.Entry> mapGenerator; private final SampleElements samples; public MapKeySetGenerator( OneSizeTestContainerGenerator, Map.Entry> mapGenerator) { this.mapGenerator = mapGenerator; final SampleElements> mapSamples = this.mapGenerator.samples(); this.samples = new SampleElements( mapSamples.e0.getKey(), mapSamples.e1.getKey(), mapSamples.e2.getKey(), mapSamples.e3.getKey(), mapSamples.e4.getKey()); } @Override public SampleElements samples() { return samples; } @Override public Set create(Object... elements) { @SuppressWarnings("unchecked") K[] keysArray = (K[]) elements; // Start with a suitably shaped collection of entries Collection> originalEntries = mapGenerator.getSampleElements(elements.length); // Create a copy of that, with the desired value for each key Collection> entries = new ArrayList>(elements.length); int i = 0; for (Map.Entry entry : originalEntries) { entries.add(Helpers.mapEntry(keysArray[i++], entry.getValue())); } return mapGenerator.create(entries.toArray()).keySet(); } @Override public K[] createArray(int length) { // TODO: with appropriate refactoring of OneSizeGenerator, we can perhaps // tidy this up and get rid of the casts here and in // MapValueCollectionGenerator. return ((TestMapGenerator) mapGenerator.getInnerGenerator()) .createKeyArray(length); } @Override public Iterable order(List insertionOrder) { return insertionOrder; } } private static class MapValueCollectionGenerator implements TestCollectionGenerator { private final OneSizeTestContainerGenerator, Map.Entry> mapGenerator; private final SampleElements samples; public MapValueCollectionGenerator( OneSizeTestContainerGenerator< Map, Map.Entry> mapGenerator) { this.mapGenerator = mapGenerator; final SampleElements> mapSamples = this.mapGenerator.samples(); this.samples = new SampleElements( mapSamples.e0.getValue(), mapSamples.e1.getValue(), mapSamples.e2.getValue(), mapSamples.e3.getValue(), mapSamples.e4.getValue()); } @Override public SampleElements samples() { return samples; } @Override public Collection create(Object... elements) { @SuppressWarnings("unchecked") V[] valuesArray = (V[]) elements; // Start with a suitably shaped collection of entries Collection> originalEntries = mapGenerator.getSampleElements(elements.length); // Create a copy of that, with the desired value for each value Collection> entries = new ArrayList>(elements.length); int i = 0; for (Map.Entry entry : originalEntries) { entries.add(Helpers.mapEntry(entry.getKey(), valuesArray[i++])); } return mapGenerator.create(entries.toArray()).values(); } @Override public V[] createArray(int length) { //noinspection UnnecessaryLocalVariable final V[] vs = ((TestMapGenerator) mapGenerator.getInnerGenerator()) .createValueArray(length); return vs; } @Override public Iterable order(List insertionOrder) { return insertionOrder; } } }