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 static com.google.common.base.Preconditions.checkArgument; 020import static com.google.common.collect.testing.Helpers.copyToSet; 021import static com.google.common.collect.testing.Helpers.mapEntry; 022import static com.google.common.testing.SerializableTester.reserialize; 023import static java.util.Collections.singleton; 024 025import com.google.common.annotations.GwtIncompatible; 026import com.google.common.collect.ImmutableList; 027import com.google.common.collect.ImmutableMultimap; 028import com.google.common.collect.Multimap; 029import com.google.common.collect.Multiset; 030import com.google.common.collect.testing.AbstractTester; 031import com.google.common.collect.testing.CollectionTestSuiteBuilder; 032import com.google.common.collect.testing.DerivedGenerator; 033import com.google.common.collect.testing.FeatureSpecificTestSuiteBuilder; 034import com.google.common.collect.testing.MapTestSuiteBuilder; 035import com.google.common.collect.testing.OneSizeTestContainerGenerator; 036import com.google.common.collect.testing.PerCollectionSizeTestSuiteBuilder; 037import com.google.common.collect.testing.SampleElements; 038import com.google.common.collect.testing.TestCollectionGenerator; 039import com.google.common.collect.testing.TestMapGenerator; 040import com.google.common.collect.testing.TestSubjectGenerator; 041import com.google.common.collect.testing.features.CollectionFeature; 042import com.google.common.collect.testing.features.CollectionSize; 043import com.google.common.collect.testing.features.Feature; 044import com.google.common.collect.testing.features.ListFeature; 045import com.google.common.collect.testing.features.MapFeature; 046import java.util.ArrayList; 047import java.util.Collection; 048import java.util.Collections; 049import java.util.EnumSet; 050import java.util.HashMap; 051import java.util.HashSet; 052import java.util.Iterator; 053import java.util.LinkedHashMap; 054import java.util.List; 055import java.util.Map; 056import java.util.Map.Entry; 057import java.util.Set; 058import junit.framework.TestSuite; 059 060/** 061 * Creates, based on your criteria, a JUnit test suite that exhaustively tests a {@code Multimap} 062 * implementation. 063 * 064 * @author Louis Wasserman 065 */ 066@GwtIncompatible 067public class MultimapTestSuiteBuilder<K, V, M extends Multimap<K, V>> 068 extends PerCollectionSizeTestSuiteBuilder< 069 MultimapTestSuiteBuilder<K, V, M>, TestMultimapGenerator<K, V, M>, M, Entry<K, V>> { 070 071 public static <K, V, M extends Multimap<K, V>> MultimapTestSuiteBuilder<K, V, M> using( 072 TestMultimapGenerator<K, V, M> generator) { 073 return new MultimapTestSuiteBuilder<K, V, M>().usingGenerator(generator); 074 } 075 076 // Class parameters must be raw. 077 @SuppressWarnings("rawtypes") // class literals 078 @Override 079 protected List<Class<? extends AbstractTester>> getTesters() { 080 return ImmutableList.of( 081 MultimapAsMapGetTester.class, 082 MultimapAsMapTester.class, 083 MultimapSizeTester.class, 084 MultimapClearTester.class, 085 MultimapContainsKeyTester.class, 086 MultimapContainsValueTester.class, 087 MultimapContainsEntryTester.class, 088 MultimapEntriesTester.class, 089 MultimapEqualsTester.class, 090 MultimapForEachTester.class, 091 MultimapGetTester.class, 092 MultimapKeySetTester.class, 093 MultimapKeysTester.class, 094 MultimapPutTester.class, 095 MultimapPutAllMultimapTester.class, 096 MultimapPutIterableTester.class, 097 MultimapReplaceValuesTester.class, 098 MultimapRemoveEntryTester.class, 099 MultimapRemoveAllTester.class, 100 MultimapToStringTester.class, 101 MultimapValuesTester.class); 102 } 103 104 @Override 105 protected List<TestSuite> createDerivedSuites( 106 FeatureSpecificTestSuiteBuilder<?, ? extends OneSizeTestContainerGenerator<M, Entry<K, V>>> 107 parentBuilder) { 108 // TODO: Once invariant support is added, supply invariants to each of the 109 // derived suites, to check that mutations to the derived collections are 110 // reflected in the underlying map. 111 112 List<TestSuite> derivedSuites = super.createDerivedSuites(parentBuilder); 113 114 if (parentBuilder.getFeatures().contains(CollectionFeature.SERIALIZABLE)) { 115 derivedSuites.add( 116 using(new ReserializedMultimapGenerator<K, V, M>(parentBuilder.getSubjectGenerator())) 117 .withFeatures(computeReserializedMultimapFeatures(parentBuilder.getFeatures())) 118 .named(parentBuilder.getName() + " reserialized") 119 .suppressing(parentBuilder.getSuppressedTests()) 120 .withSetUp(parentBuilder.getSetUp()) 121 .withTearDown(parentBuilder.getTearDown()) 122 .createTestSuite()); 123 } 124 125 derivedSuites.add( 126 MapTestSuiteBuilder.using(new AsMapGenerator<K, V, M>(parentBuilder.getSubjectGenerator())) 127 .withFeatures(computeAsMapFeatures(parentBuilder.getFeatures())) 128 .named(parentBuilder.getName() + ".asMap") 129 .suppressing(parentBuilder.getSuppressedTests()) 130 .withSetUp(parentBuilder.getSetUp()) 131 .withTearDown(parentBuilder.getTearDown()) 132 .createTestSuite()); 133 134 derivedSuites.add(computeEntriesTestSuite(parentBuilder)); 135 derivedSuites.add(computeMultimapGetTestSuite(parentBuilder)); 136 derivedSuites.add(computeMultimapAsMapGetTestSuite(parentBuilder)); 137 derivedSuites.add(computeKeysTestSuite(parentBuilder)); 138 derivedSuites.add(computeValuesTestSuite(parentBuilder)); 139 140 return derivedSuites; 141 } 142 143 TestSuite computeValuesTestSuite( 144 FeatureSpecificTestSuiteBuilder<?, ? extends OneSizeTestContainerGenerator<M, Entry<K, V>>> 145 parentBuilder) { 146 return CollectionTestSuiteBuilder.using( 147 new ValuesGenerator<K, V, M>(parentBuilder.getSubjectGenerator())) 148 .withFeatures(computeValuesFeatures(parentBuilder.getFeatures())) 149 .named(parentBuilder.getName() + ".values") 150 .suppressing(parentBuilder.getSuppressedTests()) 151 .createTestSuite(); 152 } 153 154 TestSuite computeEntriesTestSuite( 155 FeatureSpecificTestSuiteBuilder<?, ? extends OneSizeTestContainerGenerator<M, Entry<K, V>>> 156 parentBuilder) { 157 return CollectionTestSuiteBuilder.using( 158 new EntriesGenerator<K, V, M>(parentBuilder.getSubjectGenerator())) 159 .withFeatures(computeEntriesFeatures(parentBuilder.getFeatures())) 160 .named(parentBuilder.getName() + ".entries") 161 .suppressing(parentBuilder.getSuppressedTests()) 162 .withSetUp(parentBuilder.getSetUp()) 163 .withTearDown(parentBuilder.getTearDown()) 164 .createTestSuite(); 165 } 166 167 TestSuite computeMultimapGetTestSuite( 168 FeatureSpecificTestSuiteBuilder<?, ? extends OneSizeTestContainerGenerator<M, Entry<K, V>>> 169 parentBuilder) { 170 return CollectionTestSuiteBuilder.using( 171 new MultimapGetGenerator<K, V, M>(parentBuilder.getSubjectGenerator())) 172 .withFeatures(computeMultimapGetFeatures(parentBuilder.getFeatures())) 173 .named(parentBuilder.getName() + ".get[key]") 174 .suppressing(parentBuilder.getSuppressedTests()) 175 .withSetUp(parentBuilder.getSetUp()) 176 .withTearDown(parentBuilder.getTearDown()) 177 .createTestSuite(); 178 } 179 180 TestSuite computeMultimapAsMapGetTestSuite( 181 FeatureSpecificTestSuiteBuilder<?, ? extends OneSizeTestContainerGenerator<M, Entry<K, V>>> 182 parentBuilder) { 183 Set<Feature<?>> features = computeMultimapAsMapGetFeatures(parentBuilder.getFeatures()); 184 if (Collections.disjoint(features, EnumSet.allOf(CollectionSize.class))) { 185 return new TestSuite(); 186 } else { 187 return CollectionTestSuiteBuilder.using( 188 new MultimapAsMapGetGenerator<K, V, M>(parentBuilder.getSubjectGenerator())) 189 .withFeatures(features) 190 .named(parentBuilder.getName() + ".asMap[].get[key]") 191 .suppressing(parentBuilder.getSuppressedTests()) 192 .withSetUp(parentBuilder.getSetUp()) 193 .withTearDown(parentBuilder.getTearDown()) 194 .createTestSuite(); 195 } 196 } 197 198 TestSuite computeKeysTestSuite( 199 FeatureSpecificTestSuiteBuilder<?, ? extends OneSizeTestContainerGenerator<M, Entry<K, V>>> 200 parentBuilder) { 201 return MultisetTestSuiteBuilder.using( 202 new KeysGenerator<K, V, M>(parentBuilder.getSubjectGenerator())) 203 .withFeatures(computeKeysFeatures(parentBuilder.getFeatures())) 204 .named(parentBuilder.getName() + ".keys") 205 .suppressing(parentBuilder.getSuppressedTests()) 206 .withSetUp(parentBuilder.getSetUp()) 207 .withTearDown(parentBuilder.getTearDown()) 208 .createTestSuite(); 209 } 210 211 static Set<Feature<?>> computeDerivedCollectionFeatures(Set<Feature<?>> multimapFeatures) { 212 Set<Feature<?>> derivedFeatures = copyToSet(multimapFeatures); 213 if (!derivedFeatures.remove(CollectionFeature.SERIALIZABLE_INCLUDING_VIEWS)) { 214 derivedFeatures.remove(CollectionFeature.SERIALIZABLE); 215 } 216 if (derivedFeatures.remove(MapFeature.SUPPORTS_REMOVE)) { 217 derivedFeatures.add(CollectionFeature.SUPPORTS_REMOVE); 218 } 219 return derivedFeatures; 220 } 221 222 static Set<Feature<?>> computeEntriesFeatures(Set<Feature<?>> multimapFeatures) { 223 Set<Feature<?>> result = computeDerivedCollectionFeatures(multimapFeatures); 224 if (multimapFeatures.contains(MapFeature.ALLOWS_NULL_ENTRY_QUERIES)) { 225 result.add(CollectionFeature.ALLOWS_NULL_QUERIES); 226 } 227 return result; 228 } 229 230 static Set<Feature<?>> computeValuesFeatures(Set<Feature<?>> multimapFeatures) { 231 Set<Feature<?>> result = computeDerivedCollectionFeatures(multimapFeatures); 232 if (multimapFeatures.contains(MapFeature.ALLOWS_NULL_VALUES)) { 233 result.add(CollectionFeature.ALLOWS_NULL_VALUES); 234 } 235 if (multimapFeatures.contains(MapFeature.ALLOWS_NULL_VALUE_QUERIES)) { 236 result.add(CollectionFeature.ALLOWS_NULL_QUERIES); 237 } 238 return result; 239 } 240 241 static Set<Feature<?>> computeKeysFeatures(Set<Feature<?>> multimapFeatures) { 242 Set<Feature<?>> result = computeDerivedCollectionFeatures(multimapFeatures); 243 if (multimapFeatures.contains(MapFeature.ALLOWS_NULL_KEYS)) { 244 result.add(CollectionFeature.ALLOWS_NULL_VALUES); 245 } 246 if (multimapFeatures.contains(MapFeature.ALLOWS_NULL_KEY_QUERIES)) { 247 result.add(CollectionFeature.ALLOWS_NULL_QUERIES); 248 } 249 return result; 250 } 251 252 private static Set<Feature<?>> computeReserializedMultimapFeatures( 253 Set<Feature<?>> multimapFeatures) { 254 Set<Feature<?>> derivedFeatures = copyToSet(multimapFeatures); 255 derivedFeatures.remove(CollectionFeature.SERIALIZABLE); 256 derivedFeatures.remove(CollectionFeature.SERIALIZABLE_INCLUDING_VIEWS); 257 return derivedFeatures; 258 } 259 260 private static Set<Feature<?>> computeAsMapFeatures(Set<Feature<?>> multimapFeatures) { 261 Set<Feature<?>> derivedFeatures = copyToSet(multimapFeatures); 262 derivedFeatures.remove(MapFeature.GENERAL_PURPOSE); 263 derivedFeatures.remove(MapFeature.SUPPORTS_PUT); 264 derivedFeatures.remove(MapFeature.ALLOWS_NULL_VALUES); 265 derivedFeatures.add(MapFeature.ALLOWS_NULL_VALUE_QUERIES); 266 derivedFeatures.add(MapFeature.REJECTS_DUPLICATES_AT_CREATION); 267 if (!derivedFeatures.contains(CollectionFeature.SERIALIZABLE_INCLUDING_VIEWS)) { 268 derivedFeatures.remove(CollectionFeature.SERIALIZABLE); 269 } 270 return derivedFeatures; 271 } 272 273 private static final ImmutableMultimap<Feature<?>, Feature<?>> GET_FEATURE_MAP = 274 ImmutableMultimap.<Feature<?>, Feature<?>>builder() 275 .put( 276 MapFeature.FAILS_FAST_ON_CONCURRENT_MODIFICATION, 277 CollectionFeature.FAILS_FAST_ON_CONCURRENT_MODIFICATION) 278 .put(MapFeature.GENERAL_PURPOSE, ListFeature.SUPPORTS_ADD_WITH_INDEX) 279 .put(MapFeature.GENERAL_PURPOSE, ListFeature.SUPPORTS_REMOVE_WITH_INDEX) 280 .put(MapFeature.GENERAL_PURPOSE, ListFeature.SUPPORTS_SET) 281 .put(MapFeature.ALLOWS_NULL_VALUE_QUERIES, CollectionFeature.ALLOWS_NULL_QUERIES) 282 .put(MapFeature.ALLOWS_NULL_VALUES, CollectionFeature.ALLOWS_NULL_VALUES) 283 .put(MapFeature.SUPPORTS_REMOVE, CollectionFeature.SUPPORTS_REMOVE) 284 .put(MapFeature.SUPPORTS_PUT, CollectionFeature.SUPPORTS_ADD) 285 .build(); 286 287 Set<Feature<?>> computeMultimapGetFeatures(Set<Feature<?>> multimapFeatures) { 288 Set<Feature<?>> derivedFeatures = copyToSet(multimapFeatures); 289 for (Entry<Feature<?>, Feature<?>> entry : GET_FEATURE_MAP.entries()) { 290 if (derivedFeatures.contains(entry.getKey())) { 291 derivedFeatures.add(entry.getValue()); 292 } 293 } 294 if (derivedFeatures.remove(MultimapFeature.VALUE_COLLECTIONS_SUPPORT_ITERATOR_REMOVE)) { 295 derivedFeatures.add(CollectionFeature.SUPPORTS_ITERATOR_REMOVE); 296 } 297 if (!derivedFeatures.contains(CollectionFeature.SERIALIZABLE_INCLUDING_VIEWS)) { 298 derivedFeatures.remove(CollectionFeature.SERIALIZABLE); 299 } 300 derivedFeatures.removeAll(GET_FEATURE_MAP.keySet()); 301 return derivedFeatures; 302 } 303 304 Set<Feature<?>> computeMultimapAsMapGetFeatures(Set<Feature<?>> multimapFeatures) { 305 Set<Feature<?>> derivedFeatures = copyToSet(computeMultimapGetFeatures(multimapFeatures)); 306 if (derivedFeatures.remove(CollectionSize.ANY)) { 307 derivedFeatures.addAll(CollectionSize.ANY.getImpliedFeatures()); 308 } 309 derivedFeatures.remove(CollectionSize.ZERO); 310 return derivedFeatures; 311 } 312 313 private static final class AsMapGenerator<K, V, M extends Multimap<K, V>> 314 implements TestMapGenerator<K, Collection<V>>, DerivedGenerator { 315 private final OneSizeTestContainerGenerator<M, Entry<K, V>> multimapGenerator; 316 317 AsMapGenerator(OneSizeTestContainerGenerator<M, Entry<K, V>> multimapGenerator) { 318 this.multimapGenerator = multimapGenerator; 319 } 320 321 @Override 322 public TestSubjectGenerator<?> getInnerGenerator() { 323 return multimapGenerator; 324 } 325 326 private Collection<V> createCollection(V v) { 327 return ((TestMultimapGenerator<K, V, M>) multimapGenerator.getInnerGenerator()) 328 .createCollection(singleton(v)); 329 } 330 331 @Override 332 public SampleElements<Entry<K, Collection<V>>> samples() { 333 SampleElements<K> sampleKeys = 334 ((TestMultimapGenerator<K, V, M>) multimapGenerator.getInnerGenerator()).sampleKeys(); 335 SampleElements<V> sampleValues = 336 ((TestMultimapGenerator<K, V, M>) multimapGenerator.getInnerGenerator()).sampleValues(); 337 return new SampleElements<>( 338 mapEntry(sampleKeys.e0(), createCollection(sampleValues.e0())), 339 mapEntry(sampleKeys.e1(), createCollection(sampleValues.e1())), 340 mapEntry(sampleKeys.e2(), createCollection(sampleValues.e2())), 341 mapEntry(sampleKeys.e3(), createCollection(sampleValues.e3())), 342 mapEntry(sampleKeys.e4(), createCollection(sampleValues.e4()))); 343 } 344 345 @Override 346 public Map<K, Collection<V>> create(Object... elements) { 347 Set<K> keySet = new HashSet<>(); 348 List<Entry<K, V>> builder = new ArrayList<>(); 349 for (Object o : elements) { 350 Entry<?, ?> entry = (Entry<?, ?>) o; 351 // These come from Entry<K, Collection<V>>> objects somewhere. 352 @SuppressWarnings("unchecked") 353 K key = (K) entry.getKey(); 354 keySet.add(key); 355 for (Object v : (Collection<?>) entry.getValue()) { 356 // These come from Entry<K, Collection<V>>> objects somewhere. 357 @SuppressWarnings("unchecked") 358 V value = (V) v; 359 builder.add(mapEntry(key, value)); 360 } 361 } 362 checkArgument(keySet.size() == elements.length, "Duplicate keys"); 363 return multimapGenerator.create(builder.toArray()).asMap(); 364 } 365 366 @SuppressWarnings("unchecked") 367 @Override 368 public Entry<K, Collection<V>>[] createArray(int length) { 369 return (Entry<K, Collection<V>>[]) new Entry<?, ?>[length]; 370 } 371 372 @Override 373 public Iterable<Entry<K, Collection<V>>> order(List<Entry<K, Collection<V>>> insertionOrder) { 374 Map<K, Collection<V>> map = new HashMap<>(); 375 List<Entry<K, V>> builder = new ArrayList<>(); 376 for (Entry<K, Collection<V>> entry : insertionOrder) { 377 for (V v : entry.getValue()) { 378 builder.add(mapEntry(entry.getKey(), v)); 379 } 380 map.put(entry.getKey(), entry.getValue()); 381 } 382 Iterable<Entry<K, V>> ordered = multimapGenerator.order(builder); 383 LinkedHashMap<K, Collection<V>> orderedMap = new LinkedHashMap<>(); 384 for (Entry<K, V> entry : ordered) { 385 orderedMap.put(entry.getKey(), map.get(entry.getKey())); 386 } 387 return orderedMap.entrySet(); 388 } 389 390 @Override 391 public K[] createKeyArray(int length) { 392 return ((TestMultimapGenerator<K, V, M>) multimapGenerator.getInnerGenerator()) 393 .createKeyArray(length); 394 } 395 396 @SuppressWarnings("unchecked") 397 @Override 398 public Collection<V>[] createValueArray(int length) { 399 return (Collection<V>[]) new Collection<?>[length]; 400 } 401 } 402 403 static class EntriesGenerator<K, V, M extends Multimap<K, V>> 404 implements TestCollectionGenerator<Entry<K, V>>, DerivedGenerator { 405 private final OneSizeTestContainerGenerator<M, Entry<K, V>> multimapGenerator; 406 407 public EntriesGenerator(OneSizeTestContainerGenerator<M, Entry<K, V>> multimapGenerator) { 408 this.multimapGenerator = multimapGenerator; 409 } 410 411 @Override 412 public TestSubjectGenerator<?> getInnerGenerator() { 413 return multimapGenerator; 414 } 415 416 @Override 417 public SampleElements<Entry<K, V>> samples() { 418 return multimapGenerator.samples(); 419 } 420 421 @Override 422 public Collection<Entry<K, V>> create(Object... elements) { 423 return multimapGenerator.create(elements).entries(); 424 } 425 426 @SuppressWarnings("unchecked") 427 @Override 428 public Entry<K, V>[] createArray(int length) { 429 return (Entry<K, V>[]) new Entry<?, ?>[length]; 430 } 431 432 @Override 433 public Iterable<Entry<K, V>> order(List<Entry<K, V>> insertionOrder) { 434 return multimapGenerator.order(insertionOrder); 435 } 436 } 437 438 private static final class ValuesGenerator<K, V, M extends Multimap<K, V>> 439 implements TestCollectionGenerator<V> { 440 private final OneSizeTestContainerGenerator<M, Entry<K, V>> multimapGenerator; 441 442 ValuesGenerator(OneSizeTestContainerGenerator<M, Entry<K, V>> multimapGenerator) { 443 this.multimapGenerator = multimapGenerator; 444 } 445 446 @Override 447 public SampleElements<V> samples() { 448 return ((TestMultimapGenerator<K, V, M>) multimapGenerator.getInnerGenerator()) 449 .sampleValues(); 450 } 451 452 @Override 453 public Collection<V> create(Object... elements) { 454 K k = 455 ((TestMultimapGenerator<K, V, M>) multimapGenerator.getInnerGenerator()) 456 .sampleKeys() 457 .e0(); 458 Object[] entries = new Object[elements.length]; 459 for (int i = 0; i < elements.length; i++) { 460 @SuppressWarnings("unchecked") // These come from Entry<K, V> objects somewhere. 461 V value = (V) elements[i]; 462 entries[i] = mapEntry(k, value); 463 } 464 return multimapGenerator.create(entries).values(); 465 } 466 467 @Override 468 public V[] createArray(int length) { 469 return ((TestMultimapGenerator<K, V, M>) multimapGenerator.getInnerGenerator()) 470 .createValueArray(length); 471 } 472 473 @Override 474 public Iterable<V> order(List<V> insertionOrder) { 475 K k = 476 ((TestMultimapGenerator<K, V, M>) multimapGenerator.getInnerGenerator()) 477 .sampleKeys() 478 .e0(); 479 List<Entry<K, V>> entries = new ArrayList<>(); 480 for (V v : insertionOrder) { 481 entries.add(mapEntry(k, v)); 482 } 483 Iterable<Entry<K, V>> ordered = multimapGenerator.order(entries); 484 List<V> orderedValues = new ArrayList<>(); 485 for (Entry<K, V> entry : ordered) { 486 orderedValues.add(entry.getValue()); 487 } 488 return orderedValues; 489 } 490 } 491 492 private static final class KeysGenerator<K, V, M extends Multimap<K, V>> 493 implements TestMultisetGenerator<K>, DerivedGenerator { 494 private final OneSizeTestContainerGenerator<M, Entry<K, V>> multimapGenerator; 495 496 KeysGenerator(OneSizeTestContainerGenerator<M, Entry<K, V>> multimapGenerator) { 497 this.multimapGenerator = multimapGenerator; 498 } 499 500 @Override 501 public TestSubjectGenerator<?> getInnerGenerator() { 502 return multimapGenerator; 503 } 504 505 @Override 506 public SampleElements<K> samples() { 507 return ((TestMultimapGenerator<K, V, M>) multimapGenerator.getInnerGenerator()).sampleKeys(); 508 } 509 510 @Override 511 public Multiset<K> create(Object... elements) { 512 /* 513 * This is nasty and complicated, but it's the only way to make sure keys get mapped to enough 514 * distinct values. 515 */ 516 Entry<?, ?>[] entries = new Entry<?, ?>[elements.length]; 517 Map<K, Iterator<V>> valueIterators = new HashMap<>(); 518 for (int i = 0; i < elements.length; i++) { 519 @SuppressWarnings("unchecked") // These come from Entry<K, V> objects somewhere. 520 K key = (K) elements[i]; 521 522 Iterator<V> valueItr = valueIterators.get(key); 523 if (valueItr == null) { 524 valueItr = sampleValuesIterator(); 525 valueIterators.put(key, valueItr); 526 } 527 entries[i] = mapEntry(key, valueItr.next()); 528 } 529 return multimapGenerator.create((Object[]) entries).keys(); 530 } 531 532 private Iterator<V> sampleValuesIterator() { 533 return ((TestMultimapGenerator<K, V, M>) multimapGenerator.getInnerGenerator()) 534 .sampleValues() 535 .iterator(); 536 } 537 538 @Override 539 public K[] createArray(int length) { 540 return ((TestMultimapGenerator<K, V, M>) multimapGenerator.getInnerGenerator()) 541 .createKeyArray(length); 542 } 543 544 @Override 545 public Iterable<K> order(List<K> insertionOrder) { 546 Iterator<V> valueIter = sampleValuesIterator(); 547 List<Entry<K, V>> entries = new ArrayList<>(); 548 for (K k : insertionOrder) { 549 entries.add(mapEntry(k, valueIter.next())); 550 } 551 Iterable<Entry<K, V>> ordered = multimapGenerator.order(entries); 552 List<K> orderedValues = new ArrayList<>(); 553 for (Entry<K, V> entry : ordered) { 554 orderedValues.add(entry.getKey()); 555 } 556 return orderedValues; 557 } 558 } 559 560 static class MultimapGetGenerator<K, V, M extends Multimap<K, V>> 561 implements TestCollectionGenerator<V> { 562 final OneSizeTestContainerGenerator<M, Entry<K, V>> multimapGenerator; 563 564 public MultimapGetGenerator(OneSizeTestContainerGenerator<M, Entry<K, V>> multimapGenerator) { 565 this.multimapGenerator = multimapGenerator; 566 } 567 568 @Override 569 public SampleElements<V> samples() { 570 return ((TestMultimapGenerator<K, V, M>) multimapGenerator.getInnerGenerator()) 571 .sampleValues(); 572 } 573 574 @Override 575 public V[] createArray(int length) { 576 return ((TestMultimapGenerator<K, V, M>) multimapGenerator.getInnerGenerator()) 577 .createValueArray(length); 578 } 579 580 @Override 581 public Iterable<V> order(List<V> insertionOrder) { 582 K k = 583 ((TestMultimapGenerator<K, V, M>) multimapGenerator.getInnerGenerator()) 584 .sampleKeys() 585 .e0(); 586 List<Entry<K, V>> entries = new ArrayList<>(); 587 for (V v : insertionOrder) { 588 entries.add(mapEntry(k, v)); 589 } 590 Iterable<Entry<K, V>> orderedEntries = multimapGenerator.order(entries); 591 List<V> values = new ArrayList<>(); 592 for (Entry<K, V> entry : orderedEntries) { 593 values.add(entry.getValue()); 594 } 595 return values; 596 } 597 598 @Override 599 public Collection<V> create(Object... elements) { 600 Entry<K, V>[] array = multimapGenerator.createArray(elements.length); 601 K k = 602 ((TestMultimapGenerator<K, V, M>) multimapGenerator.getInnerGenerator()) 603 .sampleKeys() 604 .e0(); 605 for (int i = 0; i < elements.length; i++) { 606 @SuppressWarnings("unchecked") // These come from Entry<K, V> objects somewhere. 607 V value = (V) elements[i]; 608 array[i] = mapEntry(k, value); 609 } 610 return multimapGenerator.create((Object[]) array).get(k); 611 } 612 } 613 614 static class MultimapAsMapGetGenerator<K, V, M extends Multimap<K, V>> 615 extends MultimapGetGenerator<K, V, M> { 616 617 public MultimapAsMapGetGenerator( 618 OneSizeTestContainerGenerator<M, Entry<K, V>> multimapGenerator) { 619 super(multimapGenerator); 620 } 621 622 @Override 623 public Collection<V> create(Object... elements) { 624 Entry<K, V>[] array = multimapGenerator.createArray(elements.length); 625 K k = 626 ((TestMultimapGenerator<K, V, M>) multimapGenerator.getInnerGenerator()) 627 .sampleKeys() 628 .e0(); 629 for (int i = 0; i < elements.length; i++) { 630 @SuppressWarnings("unchecked") // These come from Entry<K, V> objects somewhere. 631 V value = (V) elements[i]; 632 array[i] = mapEntry(k, value); 633 } 634 return multimapGenerator.create((Object[]) array).asMap().get(k); 635 } 636 } 637 638 private static final class ReserializedMultimapGenerator<K, V, M extends Multimap<K, V>> 639 implements TestMultimapGenerator<K, V, M> { 640 private final OneSizeTestContainerGenerator<M, Entry<K, V>> multimapGenerator; 641 642 ReserializedMultimapGenerator(OneSizeTestContainerGenerator<M, Entry<K, V>> multimapGenerator) { 643 this.multimapGenerator = multimapGenerator; 644 } 645 646 @Override 647 public SampleElements<Entry<K, V>> samples() { 648 return multimapGenerator.samples(); 649 } 650 651 @Override 652 public Entry<K, V>[] createArray(int length) { 653 return multimapGenerator.createArray(length); 654 } 655 656 @Override 657 public Iterable<Entry<K, V>> order(List<Entry<K, V>> insertionOrder) { 658 return multimapGenerator.order(insertionOrder); 659 } 660 661 @Override 662 public M create(Object... elements) { 663 return reserialize(multimapGenerator.getInnerGenerator().create(elements)); 664 } 665 666 @Override 667 public K[] createKeyArray(int length) { 668 return ((TestMultimapGenerator<K, V, M>) multimapGenerator.getInnerGenerator()) 669 .createKeyArray(length); 670 } 671 672 @Override 673 public V[] createValueArray(int length) { 674 return ((TestMultimapGenerator<K, V, M>) multimapGenerator.getInnerGenerator()) 675 .createValueArray(length); 676 } 677 678 @Override 679 public SampleElements<K> sampleKeys() { 680 return ((TestMultimapGenerator<K, V, M>) multimapGenerator.getInnerGenerator()).sampleKeys(); 681 } 682 683 @Override 684 public SampleElements<V> sampleValues() { 685 return ((TestMultimapGenerator<K, V, M>) multimapGenerator.getInnerGenerator()) 686 .sampleValues(); 687 } 688 689 @Override 690 public Collection<V> createCollection(Iterable<? extends V> values) { 691 return ((TestMultimapGenerator<K, V, M>) multimapGenerator.getInnerGenerator()) 692 .createCollection(values); 693 } 694 } 695}