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