001/* 002 * Copyright (C) 2008 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; 018 019import static com.google.common.collect.testing.DerivedCollectionGenerators.keySetGenerator; 020import static com.google.common.collect.testing.Helpers.copyToSet; 021 022import com.google.common.annotations.GwtIncompatible; 023import com.google.common.collect.testing.DerivedCollectionGenerators.MapEntrySetGenerator; 024import com.google.common.collect.testing.DerivedCollectionGenerators.MapValueCollectionGenerator; 025import com.google.common.collect.testing.features.CollectionFeature; 026import com.google.common.collect.testing.features.CollectionSize; 027import com.google.common.collect.testing.features.Feature; 028import com.google.common.collect.testing.features.MapFeature; 029import com.google.common.collect.testing.testers.MapClearTester; 030import com.google.common.collect.testing.testers.MapContainsKeyTester; 031import com.google.common.collect.testing.testers.MapContainsValueTester; 032import com.google.common.collect.testing.testers.MapCreationTester; 033import com.google.common.collect.testing.testers.MapEntrySetTester; 034import com.google.common.collect.testing.testers.MapEqualsTester; 035import com.google.common.collect.testing.testers.MapGetTester; 036import com.google.common.collect.testing.testers.MapHashCodeTester; 037import com.google.common.collect.testing.testers.MapIsEmptyTester; 038import com.google.common.collect.testing.testers.MapPutAllTester; 039import com.google.common.collect.testing.testers.MapPutTester; 040import com.google.common.collect.testing.testers.MapRemoveTester; 041import com.google.common.collect.testing.testers.MapSerializationTester; 042import com.google.common.collect.testing.testers.MapSizeTester; 043import com.google.common.collect.testing.testers.MapToStringTester; 044import com.google.common.testing.SerializableTester; 045import java.util.Arrays; 046import java.util.HashSet; 047import java.util.List; 048import java.util.Map; 049import java.util.Map.Entry; 050import java.util.Set; 051import junit.framework.TestSuite; 052 053/** 054 * Creates, based on your criteria, a JUnit test suite that exhaustively tests a Map implementation. 055 * 056 * @author George van den Driessche 057 */ 058@GwtIncompatible 059public class MapTestSuiteBuilder<K, V> 060 extends PerCollectionSizeTestSuiteBuilder< 061 MapTestSuiteBuilder<K, V>, TestMapGenerator<K, V>, Map<K, V>, Entry<K, V>> { 062 public static <K, V> MapTestSuiteBuilder<K, V> using(TestMapGenerator<K, V> generator) { 063 return new MapTestSuiteBuilder<K, V>().usingGenerator(generator); 064 } 065 066 @SuppressWarnings("rawtypes") // class literals 067 @Override 068 protected List<Class<? extends AbstractTester>> getTesters() { 069 return Arrays.<Class<? extends AbstractTester>>asList( 070 MapClearTester.class, 071 MapContainsKeyTester.class, 072 MapContainsValueTester.class, 073 MapCreationTester.class, 074 MapEntrySetTester.class, 075 MapEqualsTester.class, 076 MapGetTester.class, 077 MapHashCodeTester.class, 078 MapIsEmptyTester.class, 079 MapPutTester.class, 080 MapPutAllTester.class, 081 MapRemoveTester.class, 082 MapSerializationTester.class, 083 MapSizeTester.class, 084 MapToStringTester.class); 085 } 086 087 @Override 088 protected List<TestSuite> createDerivedSuites( 089 FeatureSpecificTestSuiteBuilder< 090 ?, ? extends OneSizeTestContainerGenerator<Map<K, V>, Entry<K, V>>> 091 parentBuilder) { 092 // TODO: Once invariant support is added, supply invariants to each of the 093 // derived suites, to check that mutations to the derived collections are 094 // reflected in the underlying map. 095 096 List<TestSuite> derivedSuites = super.createDerivedSuites(parentBuilder); 097 098 if (parentBuilder.getFeatures().contains(CollectionFeature.SERIALIZABLE)) { 099 derivedSuites.add( 100 MapTestSuiteBuilder.using( 101 new ReserializedMapGenerator<K, V>(parentBuilder.getSubjectGenerator())) 102 .withFeatures(computeReserializedMapFeatures(parentBuilder.getFeatures())) 103 .named(parentBuilder.getName() + " reserialized") 104 .suppressing(parentBuilder.getSuppressedTests()) 105 .withSetUp(parentBuilder.getSetUp()) 106 .withTearDown(parentBuilder.getTearDown()) 107 .createTestSuite()); 108 } 109 110 derivedSuites.add( 111 createDerivedEntrySetSuite( 112 new MapEntrySetGenerator<K, V>(parentBuilder.getSubjectGenerator())) 113 .withFeatures(computeEntrySetFeatures(parentBuilder.getFeatures())) 114 .named(parentBuilder.getName() + " entrySet") 115 .suppressing(parentBuilder.getSuppressedTests()) 116 .withSetUp(parentBuilder.getSetUp()) 117 .withTearDown(parentBuilder.getTearDown()) 118 .createTestSuite()); 119 120 derivedSuites.add( 121 createDerivedKeySetSuite(keySetGenerator(parentBuilder.getSubjectGenerator())) 122 .withFeatures(computeKeySetFeatures(parentBuilder.getFeatures())) 123 .named(parentBuilder.getName() + " keys") 124 .suppressing(parentBuilder.getSuppressedTests()) 125 .withSetUp(parentBuilder.getSetUp()) 126 .withTearDown(parentBuilder.getTearDown()) 127 .createTestSuite()); 128 129 derivedSuites.add( 130 createDerivedValueCollectionSuite( 131 new MapValueCollectionGenerator<K, V>(parentBuilder.getSubjectGenerator())) 132 .named(parentBuilder.getName() + " values") 133 .withFeatures(computeValuesCollectionFeatures(parentBuilder.getFeatures())) 134 .suppressing(parentBuilder.getSuppressedTests()) 135 .withSetUp(parentBuilder.getSetUp()) 136 .withTearDown(parentBuilder.getTearDown()) 137 .createTestSuite()); 138 139 return derivedSuites; 140 } 141 142 protected SetTestSuiteBuilder<Entry<K, V>> createDerivedEntrySetSuite( 143 TestSetGenerator<Entry<K, V>> entrySetGenerator) { 144 return SetTestSuiteBuilder.using(entrySetGenerator); 145 } 146 147 protected SetTestSuiteBuilder<K> createDerivedKeySetSuite(TestSetGenerator<K> keySetGenerator) { 148 return SetTestSuiteBuilder.using(keySetGenerator); 149 } 150 151 protected CollectionTestSuiteBuilder<V> createDerivedValueCollectionSuite( 152 TestCollectionGenerator<V> valueCollectionGenerator) { 153 return CollectionTestSuiteBuilder.using(valueCollectionGenerator); 154 } 155 156 private static Set<Feature<?>> computeReserializedMapFeatures(Set<Feature<?>> mapFeatures) { 157 Set<Feature<?>> derivedFeatures = copyToSet(mapFeatures); 158 derivedFeatures.remove(CollectionFeature.SERIALIZABLE); 159 derivedFeatures.remove(CollectionFeature.SERIALIZABLE_INCLUDING_VIEWS); 160 return derivedFeatures; 161 } 162 163 private static Set<Feature<?>> computeEntrySetFeatures(Set<Feature<?>> mapFeatures) { 164 Set<Feature<?>> entrySetFeatures = computeCommonDerivedCollectionFeatures(mapFeatures); 165 if (mapFeatures.contains(MapFeature.ALLOWS_NULL_ENTRY_QUERIES)) { 166 entrySetFeatures.add(CollectionFeature.ALLOWS_NULL_QUERIES); 167 } 168 return entrySetFeatures; 169 } 170 171 private static Set<Feature<?>> computeKeySetFeatures(Set<Feature<?>> mapFeatures) { 172 Set<Feature<?>> keySetFeatures = computeCommonDerivedCollectionFeatures(mapFeatures); 173 174 // TODO(lowasser): make this trigger only if the map is a submap 175 // currently, the KeySetGenerator won't work properly for a subset of a keyset of a submap 176 keySetFeatures.add(CollectionFeature.SUBSET_VIEW); 177 if (mapFeatures.contains(MapFeature.ALLOWS_NULL_KEYS)) { 178 keySetFeatures.add(CollectionFeature.ALLOWS_NULL_VALUES); 179 } else if (mapFeatures.contains(MapFeature.ALLOWS_NULL_KEY_QUERIES)) { 180 keySetFeatures.add(CollectionFeature.ALLOWS_NULL_QUERIES); 181 } 182 183 return keySetFeatures; 184 } 185 186 private static Set<Feature<?>> computeValuesCollectionFeatures(Set<Feature<?>> mapFeatures) { 187 Set<Feature<?>> valuesCollectionFeatures = computeCommonDerivedCollectionFeatures(mapFeatures); 188 if (mapFeatures.contains(MapFeature.ALLOWS_NULL_VALUE_QUERIES)) { 189 valuesCollectionFeatures.add(CollectionFeature.ALLOWS_NULL_QUERIES); 190 } 191 if (mapFeatures.contains(MapFeature.ALLOWS_NULL_VALUES)) { 192 valuesCollectionFeatures.add(CollectionFeature.ALLOWS_NULL_VALUES); 193 } 194 195 return valuesCollectionFeatures; 196 } 197 198 public static Set<Feature<?>> computeCommonDerivedCollectionFeatures( 199 Set<Feature<?>> mapFeatures) { 200 mapFeatures = new HashSet<>(mapFeatures); 201 Set<Feature<?>> derivedFeatures = new HashSet<>(); 202 mapFeatures.remove(CollectionFeature.SERIALIZABLE); 203 if (mapFeatures.remove(CollectionFeature.SERIALIZABLE_INCLUDING_VIEWS)) { 204 derivedFeatures.add(CollectionFeature.SERIALIZABLE); 205 } 206 if (mapFeatures.contains(MapFeature.SUPPORTS_REMOVE)) { 207 derivedFeatures.add(CollectionFeature.SUPPORTS_REMOVE); 208 } 209 if (mapFeatures.contains(MapFeature.REJECTS_DUPLICATES_AT_CREATION)) { 210 derivedFeatures.add(CollectionFeature.REJECTS_DUPLICATES_AT_CREATION); 211 } 212 if (mapFeatures.contains(MapFeature.FAILS_FAST_ON_CONCURRENT_MODIFICATION)) { 213 derivedFeatures.add(CollectionFeature.FAILS_FAST_ON_CONCURRENT_MODIFICATION); 214 } 215 // add the intersection of CollectionFeature.values() and mapFeatures 216 for (CollectionFeature feature : CollectionFeature.values()) { 217 if (mapFeatures.contains(feature)) { 218 derivedFeatures.add(feature); 219 } 220 } 221 // add the intersection of CollectionSize.values() and mapFeatures 222 for (CollectionSize size : CollectionSize.values()) { 223 if (mapFeatures.contains(size)) { 224 derivedFeatures.add(size); 225 } 226 } 227 return derivedFeatures; 228 } 229 230 private static class ReserializedMapGenerator<K, V> implements TestMapGenerator<K, V> { 231 private final OneSizeTestContainerGenerator<Map<K, V>, Entry<K, V>> mapGenerator; 232 233 public ReserializedMapGenerator( 234 OneSizeTestContainerGenerator<Map<K, V>, Entry<K, V>> mapGenerator) { 235 this.mapGenerator = mapGenerator; 236 } 237 238 @Override 239 public SampleElements<Entry<K, V>> samples() { 240 return mapGenerator.samples(); 241 } 242 243 @Override 244 public Entry<K, V>[] createArray(int length) { 245 return mapGenerator.createArray(length); 246 } 247 248 @Override 249 public Iterable<Entry<K, V>> order(List<Entry<K, V>> insertionOrder) { 250 return mapGenerator.order(insertionOrder); 251 } 252 253 @Override 254 public Map<K, V> create(Object... elements) { 255 return SerializableTester.reserialize(mapGenerator.create(elements)); 256 } 257 258 @Override 259 public K[] createKeyArray(int length) { 260 return ((TestMapGenerator<K, V>) mapGenerator.getInnerGenerator()).createKeyArray(length); 261 } 262 263 @Override 264 public V[] createValueArray(int length) { 265 return ((TestMapGenerator<K, V>) mapGenerator.getInnerGenerator()).createValueArray(length); 266 } 267 } 268}