View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.collections4.map;
18  
19  import static org.junit.jupiter.api.Assertions.assertEquals;
20  import static org.junit.jupiter.api.Assertions.assertFalse;
21  import static org.junit.jupiter.api.Assertions.assertNotEquals;
22  import static org.junit.jupiter.api.Assertions.assertNotNull;
23  import static org.junit.jupiter.api.Assertions.assertNotSame;
24  import static org.junit.jupiter.api.Assertions.assertNull;
25  import static org.junit.jupiter.api.Assertions.assertThrows;
26  import static org.junit.jupiter.api.Assertions.assertTrue;
27  import static org.junit.jupiter.api.Assertions.fail;
28  import static org.junit.jupiter.api.Assumptions.assumeTrue;
29  
30  import java.io.Serializable;
31  import java.util.ArrayList;
32  import java.util.Arrays;
33  import java.util.Collection;
34  import java.util.Collections;
35  import java.util.HashMap;
36  import java.util.HashSet;
37  import java.util.Iterator;
38  import java.util.List;
39  import java.util.Map;
40  import java.util.Map.Entry;
41  import java.util.Set;
42  import java.util.concurrent.atomic.AtomicInteger;
43  import java.util.function.Function;
44  import java.util.function.Supplier;
45  
46  import org.apache.commons.collections4.AbstractObjectTest;
47  import org.apache.commons.collections4.BulkTest;
48  import org.apache.commons.collections4.CollectionUtils;
49  import org.apache.commons.collections4.collection.AbstractCollectionTest;
50  import org.apache.commons.collections4.keyvalue.DefaultMapEntry;
51  import org.apache.commons.collections4.set.AbstractSetTest;
52  import org.junit.jupiter.api.AfterEach;
53  import org.junit.jupiter.api.Test;
54  
55  /**
56   * Tests {@link java.util.Map}.
57   * <p>
58   * The forces at work here are similar to those in {@link AbstractCollectionTest}. If your class implements the full Map interface, including optional
59   * operations, simply extend this class, and implement the {@link #makeObject()} method.
60   * </p>
61   * <p>
62   * On the other hand, if your map implementation is weird, you may have to override one or more of the other protected methods. They're described below.
63   * </p>
64   * <p>
65   * <strong>Entry Population Methods</strong>
66   * </p>
67   * <p>
68   * Override these methods if your map requires special entries:
69   * </p>
70   *
71   * <ul>
72   * <li>{@link #getSampleKeys()}
73   * <li>{@link #getSampleValues()}
74   * <li>{@link #getNewSampleValues()}
75   * <li>{@link #getOtherKeys()}
76   * <li>{@link #getOtherValues()}
77   * </ul>
78   *
79   * <strong>Indicate Map Behaviour</strong>
80   * <p>
81   * Override these if your map makes specific behavior guarantees:
82   * </p>
83   * <ul>
84   * <li>{@link #getIterationBehaviour()}</li>
85   * </ul>
86   *
87   * <strong>Supported Operation Methods</strong>
88   * <p>
89   * Override these methods if your map doesn't support certain operations:
90   * </p>
91   *
92   * <ul>
93   * <li>{@link #isPutAddSupported()}
94   * <li>{@link #isPutChangeSupported()}
95   * <li>{@link #isSetValueSupported()}
96   * <li>{@link #isRemoveSupported()}
97   * <li>{@link #isGetStructuralModify()}
98   * <li>{@link #isAllowDuplicateValues()}
99   * <li>{@link #isAllowNullKey()}
100  * <li>{@link #isAllowNullValue()}
101  * </ul>
102  *
103  * <strong>Fixture Methods</strong>
104  * <p>
105  * For tests on modification operations (puts and removes), fixtures are used to verify that that operation results in correct state for the map and its
106  * collection views. Basically, the modification is performed against your map implementation, and an identical modification is performed against a
107  * <em>confirmed</em> map implementation. A confirmed map implementation is something like <Code>java.util.HashMap</Code>, which is known to conform exactly to
108  * the {@link Map} contract. After the modification takes place on both your map implementation and the confirmed map implementation, the two maps are compared
109  * to see if their state is identical. The comparison also compares the collection views to make sure they're still the same.
110  * </p>
111  * <p>
112  * The upshot of all that is that <em>any</em> test that modifies the map in <em>any</em> way will verify that <em>all</em> of the map's state is still correct,
113  * including the state of its collection views. So for instance if a key is removed by the map's key set's iterator, then the entry set is checked to make sure
114  * the key/value pair no longer appears.
115  * </p>
116  * <p>
117  * The {@link #map} field holds an instance of your collection implementation. The {@link #entrySet}, {@link #keySet} and {@link #values} fields hold that map's
118  * collection views. And the {@link #confirmed} field holds an instance of the confirmed collection implementation. The {@link #resetEmpty()} and
119  * {@link #resetFull()} methods set these fields to empty or full maps, so that tests can proceed from a known state.
120  * </p>
121  * <p>
122  * After a modification operation to both {@link #map} and {@link #confirmed}, the {@link #verify()} method is invoked to compare the results. The
123  * {@link #verify} method calls separate methods to verify the map and its three collection views ({@link #verifyMap}, {@link #verifyEntrySet},
124  * {@link #verifyKeySet}, and {@link #verifyValues}). You may want to override one of the verification methods to perform additional verifications. For
125  * instance, TestDoubleOrderedMap would want override its {@link #verifyValues()} method to verify that the values are unique and in ascending order.
126  * </p>
127  *
128  * <strong>Other Notes</strong>
129  * <p>
130  * If your {@link Map} fails one of these tests by design, you may still use this base set of cases. Simply override the test case (method) your map fails
131  * and/or the methods that define the assumptions used by the test cases. For example, if your map does not allow duplicate values, override
132  * {@link #isAllowDuplicateValues()} and have it return {@code false}
133  * </p>
134  *
135  * @param <M> the Map type.
136  * @param <K> the key type.
137  * @param <V> the value type.
138  */
139 public abstract class AbstractMapTest<M extends Map<K, V>, K, V> extends AbstractObjectTest {
140 
141     public class TestMapEntrySet extends AbstractSetTest<Map.Entry<K, V>> {
142 
143         @Override
144         public boolean areEqualElementsDistinguishable() {
145             return AbstractMapTest.this.areEqualElementsDistinguishable();
146         }
147 
148         public Map.Entry<K, V> getEntry(final Iterator<Map.Entry<K, V>> itConfirmed, final K key) {
149             Map.Entry<K, V> entry = null;
150             while (itConfirmed.hasNext()) {
151                 final Map.Entry<K, V> temp = itConfirmed.next();
152                 if (temp.getKey() == null) {
153                     if (key == null) {
154                         entry = temp;
155                         break;
156                     }
157                 } else if (temp.getKey().equals(key)) {
158                     entry = temp;
159                     break;
160                 }
161             }
162             assertNotNull(entry, "No matching entry in map for key '" + key + "'");
163             return entry;
164         }
165 
166         // Have to implement manually; entrySet doesn't support addAll
167         /**
168          * {@inheritDoc}
169          */
170         @Override
171         public Entry<K, V>[] getFullElements() {
172             return getFullNonNullElements();
173         }
174 
175         /**
176          * {@inheritDoc}
177          */
178         @Override
179         public Map.Entry<K, V>[] getFullNonNullElements() {
180             final K[] k = getSampleKeys();
181             final V[] v = getSampleValues();
182             return makeEntryArray(k, v);
183         }
184 
185         @Override
186         protected int getIterationBehaviour() {
187             return AbstractMapTest.this.getIterationBehaviour();
188         }
189 
190         // Have to implement manually; entrySet doesn't support addAll
191         @Override
192         public Map.Entry<K, V>[] getOtherElements() {
193             final K[] k = getOtherKeys();
194             final V[] v = getOtherValues();
195             return makeEntryArray(k, v);
196         }
197 
198         @Override
199         public boolean isAddSupported() {
200             // Collection views don't support add operations.
201             return false;
202         }
203 
204         public boolean isGetStructuralModify() {
205             return AbstractMapTest.this.isGetStructuralModify();
206         }
207 
208         @Override
209         public boolean isRemoveSupported() {
210             // Entry set should only support remove if map does
211             return AbstractMapTest.this.isRemoveSupported();
212         }
213 
214         @Override
215         public boolean isTestSerialization() {
216             return false;
217         }
218 
219         @Override
220         public Set<Map.Entry<K, V>> makeFullCollection() {
221             return makeFullMap().entrySet();
222         }
223 
224         @Override
225         public Set<Map.Entry<K, V>> makeObject() {
226             return AbstractMapTest.this.makeObject().entrySet();
227         }
228 
229         @Override
230         public void resetEmpty() {
231             AbstractMapTest.this.resetEmpty();
232             setCollection(AbstractMapTest.this.getMap().entrySet());
233             TestMapEntrySet.this.setConfirmed(AbstractMapTest.this.getConfirmed().entrySet());
234         }
235 
236         @Override
237         public void resetFull() {
238             AbstractMapTest.this.resetFull();
239             setCollection(AbstractMapTest.this.getMap().entrySet());
240             TestMapEntrySet.this.setConfirmed(AbstractMapTest.this.getConfirmed().entrySet());
241         }
242 
243         @Test
244         public void testMapEntrySetIteratorEntry() {
245             resetFull();
246             int count = 0;
247             for (final Entry<K, V> entry : getCollection()) {
248                 assertTrue(AbstractMapTest.this.getMap().containsKey(entry.getKey()));
249                 assertTrue(AbstractMapTest.this.getMap().containsValue(entry.getValue()));
250                 if (!isGetStructuralModify()) {
251                     assertEquals(AbstractMapTest.this.getMap().get(entry.getKey()), entry.getValue());
252                 }
253                 count++;
254             }
255             assertEquals(getCollection().size(), count);
256         }
257 
258         @Test
259         public void testMapEntrySetIteratorEntrySetValue() {
260             final K key1 = getSampleKeys()[0];
261             final K key2 = getSampleKeys().length == 1 ? getSampleKeys()[0] : getSampleKeys()[1];
262             final V newValue1 = getNewSampleValues()[0];
263             final V newValue2 = getNewSampleValues().length == 1 ? getNewSampleValues()[0] : getNewSampleValues()[1];
264 
265             resetFull();
266             // explicitly get entries as sample values/keys are connected for some maps
267             // such as BeanMap
268             Iterator<Map.Entry<K, V>> it = TestMapEntrySet.this.getCollection().iterator();
269             final Map.Entry<K, V> entry1 = getEntry(it, key1);
270             it = TestMapEntrySet.this.getCollection().iterator();
271             final Map.Entry<K, V> entry2 = getEntry(it, key2);
272             Iterator<Map.Entry<K, V>> itConfirmed = TestMapEntrySet.this.getConfirmed().iterator();
273             final Map.Entry<K, V> entryConfirmed1 = getEntry(itConfirmed, key1);
274             itConfirmed = TestMapEntrySet.this.getConfirmed().iterator();
275             final Map.Entry<K, V> entryConfirmed2 = getEntry(itConfirmed, key2);
276             verify();
277 
278             if (!isSetValueSupported()) {
279                 assertThrows(UnsupportedOperationException.class, () -> entry1.setValue(newValue1));
280                 return;
281             }
282 
283             entry1.setValue(newValue1);
284             entryConfirmed1.setValue(newValue1);
285             assertEquals(newValue1, entry1.getValue());
286             assertTrue(AbstractMapTest.this.getMap().containsKey(entry1.getKey()));
287             assertTrue(AbstractMapTest.this.getMap().containsValue(newValue1));
288             assertEquals(newValue1, AbstractMapTest.this.getMap().get(entry1.getKey()));
289             verify();
290 
291             entry1.setValue(newValue1);
292             entryConfirmed1.setValue(newValue1);
293             assertEquals(newValue1, entry1.getValue());
294             assertTrue(AbstractMapTest.this.getMap().containsKey(entry1.getKey()));
295             assertTrue(AbstractMapTest.this.getMap().containsValue(newValue1));
296             assertEquals(newValue1, AbstractMapTest.this.getMap().get(entry1.getKey()));
297             verify();
298 
299             entry2.setValue(newValue2);
300             entryConfirmed2.setValue(newValue2);
301             assertEquals(newValue2, entry2.getValue());
302             assertTrue(AbstractMapTest.this.getMap().containsKey(entry2.getKey()));
303             assertTrue(AbstractMapTest.this.getMap().containsValue(newValue2));
304             assertEquals(newValue2, AbstractMapTest.this.getMap().get(entry2.getKey()));
305             verify();
306         }
307 
308         @Test
309         public void testMapEntrySetRemoveNonMapEntry() {
310             if (!isRemoveSupported()) {
311                 return;
312             }
313             resetFull();
314             assertFalse(getCollection().remove(null));
315             assertFalse(getCollection().remove(new Object()));
316         }
317 
318         @Override
319         public void verify() {
320             super.verify();
321             AbstractMapTest.this.verify();
322         }
323     }
324 
325     public class TestMapKeySet extends AbstractSetTest<K> {
326 
327         @Override
328         public K[] getFullElements() {
329             return getSampleKeys();
330         }
331 
332         @Override
333         protected int getIterationBehaviour() {
334             return AbstractMapTest.this.getIterationBehaviour();
335         }
336 
337         @Override
338         public K[] getOtherElements() {
339             return getOtherKeys();
340         }
341 
342         @Override
343         public boolean isAddSupported() {
344             return false;
345         }
346 
347         @Override
348         public boolean isNullSupported() {
349             return AbstractMapTest.this.isAllowNullKey();
350         }
351 
352         @Override
353         public boolean isRemoveSupported() {
354             return AbstractMapTest.this.isRemoveSupported();
355         }
356 
357         @Override
358         public boolean isTestSerialization() {
359             return false;
360         }
361 
362         @Override
363         public Set<K> makeFullCollection() {
364             return AbstractMapTest.this.makeFullMap().keySet();
365         }
366 
367         @Override
368         public Set<K> makeObject() {
369             return AbstractMapTest.this.makeObject().keySet();
370         }
371 
372         @Override
373         public void resetEmpty() {
374             AbstractMapTest.this.resetEmpty();
375             setCollection(AbstractMapTest.this.getMap().keySet());
376             TestMapKeySet.this.setConfirmed(AbstractMapTest.this.getConfirmed().keySet());
377         }
378 
379         @Override
380         public void resetFull() {
381             AbstractMapTest.this.resetFull();
382             setCollection(AbstractMapTest.this.getMap().keySet());
383             TestMapKeySet.this.setConfirmed(AbstractMapTest.this.getConfirmed().keySet());
384         }
385 
386         @Override
387         public void verify() {
388             super.verify();
389             AbstractMapTest.this.verify();
390         }
391 
392     }
393 
394     // These instance variables are initialized with the reset method.
395     // Tests for map methods that alter the map (put, putAll, remove)
396     // first call reset() to create the map and its views; then perform
397     // the modification on the map; perform the same modification on the
398     // confirmed; and then call verify() to ensure that the map is equal
399     // to the confirmed, that the already-constructed collection views
400     // are still equal to the confirmed's collection views.
401 
402     public class TestMapValues extends AbstractCollectionTest<V> {
403 
404         @Override
405         public boolean areEqualElementsDistinguishable() {
406             // equal values are associated with different keys, so they are
407             // distinguishable.
408             return true;
409         }
410 
411         @Override
412         public V[] getFullElements() {
413             return getSampleValues();
414         }
415 
416         @Override
417         protected int getIterationBehaviour() {
418             return AbstractMapTest.this.getIterationBehaviour();
419         }
420 
421         @Override
422         public V[] getOtherElements() {
423             return getOtherValues();
424         }
425 
426         @Override
427         public boolean isAddSupported() {
428             return false;
429         }
430 
431         @Override
432         public boolean isNullSupported() {
433             return AbstractMapTest.this.isAllowNullKey();
434         }
435 
436         @Override
437         public boolean isRemoveSupported() {
438             return AbstractMapTest.this.isRemoveSupported();
439         }
440 
441         @Override
442         public boolean isTestSerialization() {
443             return false;
444         }
445 
446         @Override
447         public Collection<V> makeConfirmedCollection() {
448             // never gets called, reset methods are overridden
449             return null;
450         }
451 
452         @Override
453         public Collection<V> makeConfirmedFullCollection() {
454             // never gets called, reset methods are overridden
455             return null;
456         }
457 
458         @Override
459         public Collection<V> makeFullCollection() {
460             return AbstractMapTest.this.makeFullMap().values();
461         }
462 
463         @Override
464         public Collection<V> makeObject() {
465             return AbstractMapTest.this.makeObject().values();
466         }
467 
468         @Override
469         public void resetEmpty() {
470             AbstractMapTest.this.resetEmpty();
471             setCollection(map.values());
472             TestMapValues.this.setConfirmed(AbstractMapTest.this.getConfirmed().values());
473         }
474 
475         @Override
476         public void resetFull() {
477             AbstractMapTest.this.resetFull();
478             setCollection(map.values());
479             TestMapValues.this.setConfirmed(AbstractMapTest.this.getConfirmed().values());
480         }
481 
482         @Override
483         public void verify() {
484             super.verify();
485             AbstractMapTest.this.verify();
486         }
487 
488         // TODO: should test that a remove on the values collection view
489         // removes the proper mapping and not just any mapping that may have
490         // the value equal to the value returned from the values iterator.
491     }
492 
493     /**
494      * Creates a new Map Entry that is independent of the first and the map.
495      *
496      * @param <K> the key type.
497      * @param <V> the value type.
498      */
499     public static <K, V> Map.Entry<K, V> cloneMapEntry(final Map.Entry<K, V> entry) {
500         final HashMap<K, V> map = new HashMap<>();
501         map.put(entry.getKey(), entry.getValue());
502         return map.entrySet().iterator().next();
503     }
504 
505     /** Map created by reset(). */
506     protected M map;
507 
508     /** Entry set of map created by reset(). */
509     protected Set<Map.Entry<K, V>> entrySet;
510 
511     /** Key set of map created by reset(). */
512     protected Set<K> keySet;
513 
514     /** Values collection of map created by reset(). */
515     protected Collection<V> values;
516 
517     /** HashMap created by reset(). */
518     protected Map<K, V> confirmed;
519 
520     /**
521      * Helper method to add all the mappings described by {@link #getSampleKeys()} and {@link #getSampleValues()}.
522      */
523     public void addSampleMappings(final Map<? super K, ? super V> m) {
524 
525         final K[] keys = getSampleKeys();
526         final V[] values = getSampleValues();
527 
528         for (int i = 0; i < keys.length; i++) {
529             try {
530                 m.put(keys[i], values[i]);
531             } catch (final NullPointerException exception) {
532                 assertTrue(keys[i] == null || values[i] == null, "NullPointerException only allowed to be thrown " + "if either the key or value is null.");
533 
534                 assertTrue(keys[i] == null || !isAllowNullKey(),
535                         "NullPointerException on null key, but " + "isAllowNullKey is not overridden to return false.");
536 
537                 assertTrue(values[i] == null || !isAllowNullValue(),
538                         "NullPointerException on null value, but " + "isAllowNullValue is not overridden to return false.");
539 
540                 fail("Unknown reason for NullPointer.");
541             }
542         }
543         assertEquals(keys.length, m.size(), "size must reflect number of mappings added.");
544     }
545 
546     public boolean areEqualElementsDistinguishable() {
547         return false;
548     }
549 
550     /**
551      * Bulk test {@link Map#entrySet()}. This method runs through all of the tests in {@link AbstractSetTest}. After modification operations, {@link #verify()}
552      * is invoked to ensure that the map and the other collection views are still valid.
553      *
554      * @return a {@link AbstractSetTest} instance for testing the map's entry set
555      */
556     public BulkTest bulkTestMapEntrySet() {
557         return new TestMapEntrySet();
558     }
559 
560     /**
561      * Bulk test {@link Map#keySet()}. This method runs through all of the tests in {@link AbstractSetTest}. After modification operations, {@link #verify()} is
562      * invoked to ensure that the map and the other collection views are still valid.
563      *
564      * @return a {@link AbstractSetTest} instance for testing the map's key set
565      */
566     public BulkTest bulkTestMapKeySet() {
567         return new TestMapKeySet();
568     }
569 
570     /**
571      * Bulk test {@link Map#values()}. This method runs through all of the tests in {@link AbstractCollectionTest}. After modification operations,
572      * {@link #verify()} is invoked to ensure that the map and the other collection views are still valid.
573      *
574      * @return a {@link AbstractCollectionTest} instance for testing the map's values collection
575      */
576     public BulkTest bulkTestMapValues() {
577         return new TestMapValues();
578     }
579 
580     /**
581      * Subclasses can override for special cases, like Apache Commons BeanUtils.
582      *
583      * @param key             See @{link {@link Map#computeIfAbsent(Object, Function)}.
584      * @param mappingFunction See @{link {@link Map#computeIfAbsent(Object, Function)}.
585      * @return See @{link {@link Map#computeIfAbsent(Object, Function)}.
586      */
587     protected V computeIfAbsent(final K key, final Function<? super K, ? extends V> mappingFunction) {
588         return getMap().computeIfAbsent(key, mappingFunction);
589     }
590 
591     @SuppressWarnings("unchecked")
592     protected <E> List<E> getAsList(final Object[] o) {
593         final ArrayList<E> result = new ArrayList<>();
594         for (final Object element : o) {
595             result.add((E) element);
596         }
597         return result;
598     }
599 
600     /**
601      * Gets the compatibility version, needed for package access.
602      */
603     @Override
604     public String getCompatibilityVersion() {
605         return super.getCompatibilityVersion();
606     }
607 
608     /**
609      * Gets the confirmed.
610      *
611      * @return Map<K, V>
612      */
613     public Map<K, V> getConfirmed() {
614         return confirmed;
615     }
616 
617     /**
618      * Return a flag specifying the iteration behavior of the collection. This is used to change the assertions used by specific tests. The default
619      * implementation returns 0 which indicates ordered iteration behavior.
620      *
621      * @return the iteration behavior
622      * @see AbstractCollectionTest#UNORDERED
623      */
624     protected int getIterationBehaviour() {
625         return 0;
626     }
627 
628     /**
629      * Gets the map.
630      *
631      * @return Map<K, V>
632      */
633     public M getMap() {
634         return map;
635     }
636 
637     /**
638      * Returns a set of values that can be used to replace the values returned from {@link #getSampleValues()}. This method must return an array with the same
639      * length as {@link #getSampleValues()}. The values returned from this method should not be the same as those returned from {@link #getSampleValues()}. The
640      * default implementation constructs a set of String values and includes a single null value if {@link #isAllowNullValue()} returns {@code true}, and
641      * includes two values that are the same if {@link #isAllowDuplicateValues()} returns {@code true}.
642      */
643     @SuppressWarnings("unchecked")
644     public V[] getNewSampleValues() {
645         final Object[] result = { isAllowNullValue() && isAllowDuplicateValues() ? null : "newnonnullvalue", "newvalue",
646                 isAllowDuplicateValues() ? "newvalue" : "newvalue2", "newblahv", "newfoov", "newbarv", "newbazv", "newtmpv", "newgoshv", "newgollyv", "newgeev",
647                 "newhellov", "newgoodbyev", "newwe'llv", "newseev", "newyouv", "newallv", "newagainv" };
648         return (V[]) result;
649     }
650 
651     @SuppressWarnings("unchecked")
652     public K[] getOtherKeys() {
653         return (K[]) getOtherNonNullStringElements();
654     }
655 
656     /**
657      * Returns a list of string elements suitable for return by {@link #getOtherKeys()} or {@link #getOtherValues}.
658      *
659      * <p>
660      * Override getOtherElements to return the results of this method if your collection does not support heterogeneous elements or the null element.
661      * </p>
662      */
663     public Object[] getOtherNonNullStringElements() {
664         return new Object[] { "For", "then", "despite", /* of */"space", "I", "would", "be", "brought", "From", "limits", "far", "remote", "where", "thou",
665                 "dost", "stay" };
666     }
667 
668     @SuppressWarnings("unchecked")
669     public V[] getOtherValues() {
670         return (V[]) getOtherNonNullStringElements();
671     }
672 
673     /**
674      * Returns the set of keys in the mappings used to test the map. This method must return an array with the same length as {@link #getSampleValues()} and all
675      * array elements must be different. The default implementation constructs a set of String keys, and includes a single null key if {@link #isAllowNullKey()}
676      * returns {@code true}.
677      */
678     @SuppressWarnings("unchecked")
679     public K[] getSampleKeys() {
680         final Object[] result = { "blah", "foo", "bar", "baz", "tmp", "gosh", "golly", "gee", "hello", "goodbye", "we'll", "see", "you", "all", "again", "key",
681                 "key2", isAllowNullKey() ? null : "nonnullkey" };
682         return (K[]) result;
683     }
684 
685     /**
686      * Returns the set of values in the mappings used to test the map. This method must return an array with the same length as {@link #getSampleKeys()}. The
687      * default implementation constructs a set of String values and includes a single null value if {@link #isAllowNullValue()} returns {@code true}, and
688      * includes two values that are the same if {@link #isAllowDuplicateValues()} returns {@code true}.
689      */
690     @SuppressWarnings("unchecked")
691     public V[] getSampleValues() {
692         final Object[] result = { "blahv", "foov", "barv", "bazv", "tmpv", "goshv", "gollyv", "geev", "hellov", "goodbyev", "we'llv", "seev", "youv", "allv",
693                 "againv", isAllowNullValue() ? null : "nonnullvalue", "value", isAllowDuplicateValues() ? "value" : "value2", };
694         return (V[]) result;
695     }
696 
697     /**
698      * Returns true if the maps produced by {@link #makeObject()} and {@link #makeFullMap()} supports duplicate values.
699      * <p>
700      * Default implementation returns true. Override if your collection class does not support duplicate values.
701      * </p>
702      *
703      * @return true by default.
704      */
705     public boolean isAllowDuplicateValues() {
706         return true;
707     }
708 
709     /**
710      * Returns true if the maps produced by {@link #makeObject()} and {@link #makeFullMap()} supports null keys.
711      * <p>
712      * Default implementation returns true. Override if your collection class does not support null keys.
713      * </p>
714      *
715      * @return true by default.
716      */
717     public boolean isAllowNullKey() {
718         return true;
719     }
720 
721     /**
722      * Returns true if the maps produced by {@link #makeObject()} and {@link #makeFullMap()} supports null values.
723      * <p>
724      * Default implementation returns true. Override if your collection class does not support null values.
725      * </p>
726      */
727     public boolean isAllowNullValue() {
728         return isAllowNullValueGet() && isAllowNullValuePut();
729     }
730 
731     /**
732      * Returns true if the maps produced by {@link #makeObject()} and {@link #makeFullMap()} supports null values.
733      * <p>
734      * Default implementation returns true. Override if your collection class does not support null values.
735      * </p>
736      *
737      * @return true by default.
738      */
739     public boolean isAllowNullValueGet() {
740         return true;
741     }
742 
743     /**
744      * Returns true if the maps produced by {@link #makeObject()} and {@link #makeFullMap()} supports null values.
745      * <p>
746      * Default implementation returns true. Override if your collection class does not support null values.
747      * </p>
748      *
749      * @return true by default.
750      */
751     public boolean isAllowNullValuePut() {
752         return true;
753     }
754 
755     /**
756      * Returns true if the maps produced by {@link #makeObject()} and {@link #makeFullMap()} provide fail-fast behavior on their various iterators.
757      * <p>
758      * Default implementation returns true. Override if your collection class does not support fast failure.
759      * </p>
760      *
761      * @return true by default.
762      */
763     public boolean isFailFastExpected() {
764         return true;
765     }
766 
767     /**
768      * Returns true if the maps produced by {@link #makeObject()} and {@link #makeFullMap()} can cause structural modification on a get(). The example is
769      * LRUMap.
770      * <p>
771      * Default implementation returns false. Override if your map class structurally modifies on get.
772      * </p>
773      *
774      * @return false by default.
775      */
776     public boolean isGetStructuralModify() {
777         return false;
778     }
779 
780     // tests begin here. Each test adds a little bit of tested functionality.
781     // Many methods assume previous methods passed. That is, they do not
782     // exhaustively recheck things that have already been checked in a previous
783     // test methods.
784 
785     protected boolean isLazyMapTest() {
786         return false;
787     }
788 
789     /**
790      * Returns true if the maps produced by {@link #makeObject()} and {@link #makeFullMap()} support the {@link Map#put(Object, Object)} and
791      * {@link Map#putAll(Map)} operations adding new mappings.
792      * <p>
793      * Default implementation returns true. Override if your collection class does not support put adding.
794      * </p>
795      *
796      * @return true by default.
797      */
798     public boolean isPutAddSupported() {
799         return true;
800     }
801 
802     /**
803      * Returns true if the maps produced by {@link #makeObject()} and {@link #makeFullMap()} support the {@link Map#put(Object, Object)} and
804      * {@link Map#putAll(Map)} operations changing existing mappings.
805      * <p>
806      * Default implementation returns true. Override if your collection class does not support put changing.
807      * </p>
808      *
809      * @return true by default.
810      */
811     public boolean isPutChangeSupported() {
812         return true;
813     }
814 
815     /**
816      * Returns true if the maps produced by {@link #makeObject()} and {@link #makeFullMap()} support the {@code remove} and {@link Map#clear()} operations.
817      * <p>
818      * Default implementation returns true. Override if your collection class does not support removal operations.
819      * </p>
820      *
821      * @return true by default.
822      */
823     public boolean isRemoveSupported() {
824         return true;
825     }
826 
827     /**
828      * Returns true if the maps produced by {@link #makeObject()} and {@link #makeFullMap()} support the {@link Map#replaceAll(java.util.function.BiFunction)}
829      * operations.
830      * <p>
831      * Default implementation returns true. Override if your collection class does not support replaceAll operations.
832      * </p>
833      *
834      * @return true by default.
835      */
836     public boolean isReplaceAllSupported() {
837         return true;
838     }
839 
840     /**
841      * Returns true if the maps produced by {@link #makeObject()} and {@link #makeFullMap()} support the {@code setValue} operation on entrySet entries.
842      * <p>
843      * Default implementation returns isPutChangeSupported(). Override if your collection class does not support setValue but does support put changing.
844      * </p>
845      */
846     public boolean isSetValueSupported() {
847         return isPutChangeSupported();
848     }
849 
850     /**
851      * Returns whether the sub map views of SortedMap are serializable. If the class being tested is based around a TreeMap then you should override and return
852      * false as TreeMap has a bug in deserialization.
853      *
854      * @return true by default.
855      */
856     public boolean isSubMapViewsSerializable() {
857         return true;
858     }
859 
860     /**
861      * Override to return a map other than HashMap as the confirmed map.
862      *
863      * @return a map that is known to be valid
864      */
865     public Map<K, V> makeConfirmedMap() {
866         return new HashMap<>();
867     }
868 
869     /**
870      * Utility methods to create an array of Map.Entry objects out of the given key and value arrays.
871      *
872      * @param keys   the array of keys
873      * @param values the array of values
874      * @return an array of Map.Entry of those keys to those values
875      */
876     @SuppressWarnings("unchecked")
877     private Map.Entry<K, V>[] makeEntryArray(final K[] keys, final V[] values) {
878         final Map.Entry<K, V>[] result = new Map.Entry[keys.length];
879         for (int i = 0; i < keys.length; i++) {
880             final Map<K, V> map = makeConfirmedMap();
881             map.put(keys[i], values[i]);
882             result[i] = map.entrySet().iterator().next();
883         }
884         return result;
885     }
886 
887     /**
888      * Return a new, populated map. The mappings in the map should match the keys and values returned from {@link #getSampleKeys()} and
889      * {@link #getSampleValues()}. The default implementation uses makeEmptyMap() and calls {@link #addSampleMappings} to add all the mappings to the map.
890      *
891      * @return the map to be tested
892      */
893     public M makeFullMap() {
894         final M m = makeObject();
895         addSampleMappings(m);
896         return m;
897     }
898 
899     /**
900      * Return a new, empty {@link Map} to be used for testing.
901      *
902      * @return the map to be tested
903      */
904     @Override
905     public abstract M makeObject();
906 
907     /**
908      * Subclasses can override for special cases, like Apache Commons BeanUtils.
909      *
910      * @param key   See @{link {@link Map#putIfAbsent(Object, Object)}.
911      * @param value See @{link {@link Map#putIfAbsent(Object, Object)}.
912      * @return See @{link {@link Map#putIfAbsent(Object, Object)}.
913      */
914     protected V putIfAbsent(final K key, final V value) {
915         return getMap().putIfAbsent(key, value);
916     }
917 
918     /**
919      * Resets the {@link #map}, {@link #entrySet}, {@link #keySet}, {@link #values} and {@link #confirmed} fields to empty.
920      */
921     public void resetEmpty() {
922         map = makeObject();
923         views();
924         confirmed = makeConfirmedMap();
925     }
926 
927     /**
928      * Resets the {@link #map}, {@link #entrySet}, {@link #keySet}, {@link #values} and {@link #confirmed} fields to full.
929      */
930     public void resetFull() {
931         map = makeFullMap();
932         views();
933         confirmed = makeConfirmedMap();
934         final K[] k = getSampleKeys();
935         final V[] v = getSampleValues();
936         for (int i = 0; i < k.length; i++) {
937             confirmed.put(k[i], v[i]);
938         }
939     }
940 
941     /**
942      * Erases any leftover instance variables by setting them to null.
943      */
944     @AfterEach
945     public void tearDown() throws Exception {
946         map = null;
947         keySet = null;
948         entrySet = null;
949         values = null;
950         confirmed = null;
951     }
952 
953     /**
954      * Compare the current serialized form of the Map against the canonical version in SCM.
955      */
956     @Test
957     public void testEmptyMapCompatibility() throws Exception {
958         /*
959          * Create canonical objects with this code Map map = makeEmptyMap(); if (!(map instanceof Serializable)) return;
960          *
961          * writeExternalFormToDisk((Serializable) map, getCanonicalEmptyCollectionName(map));
962          */
963 
964         // test to make sure the canonical form has been preserved
965         final Map<K, V> map = makeObject();
966         if (map instanceof Serializable && !skipSerializedCanonicalTests() && isTestSerialization()) {
967             @SuppressWarnings("unchecked")
968             final Map<K, V> map2 = (Map<K, V>) readExternalFormFromDisk(getCanonicalEmptyCollectionName(map));
969             assertEquals(0, map2.size(), "Map is empty");
970         }
971     }
972 
973     /**
974      * Tests that the {@link Map#entrySet()} collection is backed by the underlying map for clear().
975      */
976     @Test
977     public void testEntrySetClearChangesMap() {
978         if (!isRemoveSupported()) {
979             return;
980         }
981 
982         // clear values, reflected in map
983         resetFull();
984         Set<Map.Entry<K, V>> entrySet = getMap().entrySet();
985         assertFalse(getMap().isEmpty());
986         assertFalse(entrySet.isEmpty());
987         entrySet.clear();
988         assertTrue(getMap().isEmpty());
989         assertTrue(entrySet.isEmpty());
990 
991         // clear map, reflected in values
992         resetFull();
993         entrySet = getMap().entrySet();
994         assertFalse(getMap().isEmpty());
995         assertFalse(entrySet.isEmpty());
996         getMap().clear();
997         assertTrue(getMap().isEmpty());
998         assertTrue(entrySet.isEmpty());
999     }
1000 
1001     @Test
1002     public void testEntrySetContains1() {
1003         resetFull();
1004         final Set<Map.Entry<K, V>> entrySet = getMap().entrySet();
1005         final Map.Entry<K, V> entry = entrySet.iterator().next();
1006         assertTrue(entrySet.contains(entry));
1007     }
1008 
1009     @Test
1010     public void testEntrySetContains2() {
1011         resetFull();
1012         final Set<Map.Entry<K, V>> entrySet = getMap().entrySet();
1013         final Map.Entry<K, V> entry = entrySet.iterator().next();
1014         final Map.Entry<K, V> test = cloneMapEntry(entry);
1015         assertTrue(entrySet.contains(test));
1016     }
1017 
1018     @Test
1019     @SuppressWarnings("unchecked")
1020     public void testEntrySetContains3() {
1021         resetFull();
1022         final Set<Map.Entry<K, V>> entrySet = getMap().entrySet();
1023         final Map.Entry<K, V> entry = entrySet.iterator().next();
1024         final HashMap<K, V> temp = new HashMap<>();
1025         temp.put(entry.getKey(), (V) "A VERY DIFFERENT VALUE");
1026         final Map.Entry<K, V> test = temp.entrySet().iterator().next();
1027         assertFalse(entrySet.contains(test));
1028     }
1029 
1030     /**
1031      * Verify that entrySet.iterator.remove changes the underlying map.
1032      */
1033     @Test
1034     public void testEntrySetIteratorRemoveChangesMap() {
1035         resetFull();
1036         for (final Iterator<Map.Entry<K, V>> iter = getMap().entrySet().iterator(); iter.hasNext();) {
1037             final K key = iter.next().getKey();
1038             try {
1039                 iter.remove();
1040             } catch (final UnsupportedOperationException e) {
1041                 return;
1042             }
1043             assertFalse(getMap().containsKey(key));
1044         }
1045     }
1046 
1047     @Test
1048     public void testEntrySetRemove1() {
1049         if (!isRemoveSupported()) {
1050             return;
1051         }
1052         resetFull();
1053         final int size = getMap().size();
1054         final Set<Map.Entry<K, V>> entrySet = getMap().entrySet();
1055         final Map.Entry<K, V> entry = entrySet.iterator().next();
1056         final K key = entry.getKey();
1057 
1058         assertTrue(entrySet.remove(entry));
1059         assertFalse(getMap().containsKey(key));
1060         assertEquals(size - 1, getMap().size());
1061     }
1062 
1063     @Test
1064     public void testEntrySetRemove2() {
1065         if (!isRemoveSupported()) {
1066             return;
1067         }
1068         resetFull();
1069         final int size = getMap().size();
1070         final Set<Map.Entry<K, V>> entrySet = getMap().entrySet();
1071         final Map.Entry<K, V> entry = entrySet.iterator().next();
1072         final K key = entry.getKey();
1073         final Map.Entry<K, V> test = cloneMapEntry(entry);
1074 
1075         assertTrue(entrySet.remove(test));
1076         assertFalse(getMap().containsKey(key));
1077         assertEquals(size - 1, getMap().size());
1078     }
1079 
1080     @Test
1081     @SuppressWarnings("unchecked")
1082     public void testEntrySetRemove3() {
1083         if (!isRemoveSupported()) {
1084             return;
1085         }
1086         resetFull();
1087         final int size = getMap().size();
1088         final Set<Map.Entry<K, V>> entrySet = getMap().entrySet();
1089         final Map.Entry<K, V> entry = entrySet.iterator().next();
1090         final K key = entry.getKey();
1091         final HashMap<K, V> temp = new HashMap<>();
1092         temp.put(entry.getKey(), (V) "A VERY DIFFERENT VALUE");
1093         final Map.Entry<K, V> test = temp.entrySet().iterator().next();
1094 
1095         assertFalse(entrySet.remove(test));
1096         assertTrue(getMap().containsKey(key));
1097         assertEquals(size, getMap().size());
1098     }
1099 
1100     /**
1101      * Test entrySet.removeAll.
1102      */
1103     @Test
1104     public void testEntrySetRemoveAll() {
1105         resetFull();
1106         final K[] sampleKeys = getSampleKeys();
1107         final V[] sampleValues = getSampleValues();
1108         // verify map looks as expected:
1109         for (int i = 0; i < sampleKeys.length; i++) {
1110             if (!getMap().containsKey(sampleKeys[i])) {
1111                 return;
1112             }
1113             final V value = sampleValues[i];
1114             final V test = getMap().get(sampleKeys[i]);
1115             if (value == test || value != null && value.equals(test)) {
1116                 continue;
1117             }
1118             return;
1119         }
1120         final Set<Map.Entry<K, V>> entrySet = getMap().entrySet();
1121         final HashSet<Map.Entry<K, V>> comparisonSet = new HashSet<>(entrySet);
1122         try {
1123             assertFalse(entrySet.removeAll(Collections.<Map.Entry<K, V>>emptySet()));
1124         } catch (final UnsupportedOperationException e) {
1125             return;
1126         }
1127         assertEquals(sampleKeys.length, getMap().size());
1128         try {
1129             assertTrue(entrySet.removeAll(comparisonSet));
1130         } catch (final UnsupportedOperationException e) {
1131             return;
1132         }
1133         assertTrue(getMap().isEmpty());
1134     }
1135 
1136     /**
1137      * Tests that the {@link Map#entrySet} set is backed by the underlying map by removing from the entrySet set and testing if the entry was removed from the
1138      * map.
1139      */
1140     @Test
1141     public void testEntrySetRemoveChangesMap() {
1142         resetFull();
1143         final K[] sampleKeys = getSampleKeys();
1144         final V[] sampleValues = getSampleValues();
1145         final Set<Map.Entry<K, V>> entrySet = getMap().entrySet();
1146         for (int i = 0; i < sampleKeys.length; i++) {
1147             try {
1148                 entrySet.remove(new DefaultMapEntry<>(sampleKeys[i], sampleValues[i]));
1149             } catch (final UnsupportedOperationException e) {
1150                 // if entrySet removal is unsupported, just skip this test
1151                 return;
1152             }
1153             assertFalse(getMap().containsKey(sampleKeys[i]), "Entry should have been removed from the underlying map.");
1154         }
1155     }
1156 
1157     /**
1158      * Test entrySet.retainAll.
1159      */
1160     @Test
1161     public void testEntrySetRetainAll() {
1162         resetFull();
1163         final K[] sampleKeys = getSampleKeys();
1164         final V[] sampleValues = getSampleValues();
1165         // verify map looks as expected:
1166         for (int i = 0; i < sampleKeys.length; i++) {
1167             if (!getMap().containsKey(sampleKeys[i])) {
1168                 return;
1169             }
1170             final V value = sampleValues[i];
1171             final V test = getMap().get(sampleKeys[i]);
1172             if (value == test || value != null && value.equals(test)) {
1173                 continue;
1174             }
1175             return;
1176         }
1177         final Set<Map.Entry<K, V>> entrySet = getMap().entrySet();
1178         final HashSet<Map.Entry<K, V>> comparisonSet = new HashSet<>(entrySet);
1179         try {
1180             assertFalse(entrySet.retainAll(comparisonSet));
1181         } catch (final UnsupportedOperationException e) {
1182             return;
1183         }
1184         assertEquals(sampleKeys.length, getMap().size());
1185         try {
1186             assertTrue(entrySet.retainAll(Collections.<Map.Entry<K, V>>emptySet()));
1187         } catch (final UnsupportedOperationException e) {
1188             return;
1189         }
1190         assertTrue(getMap().isEmpty());
1191     }
1192 
1193     /**
1194      * Tests {@link Map#forEach(java.util.function.BiConsumer)}.
1195      */
1196     @Test
1197     public void testForEach() {
1198         resetFull();
1199         final AtomicInteger i = new AtomicInteger();
1200         getMap().forEach((k, v) -> {
1201             assertTrue(getMap().containsKey(k));
1202             assertTrue(getMap().containsValue(v));
1203             i.incrementAndGet();
1204         });
1205         assertEquals(i.get(), getMap().size());
1206     }
1207 
1208     /**
1209      * Compare the current serialized form of the Map against the canonical version in SCM.
1210      */
1211     @Test
1212     public void testFullMapCompatibility() throws Exception {
1213         /*
1214          * Create canonical objects with this code Map map = makeFullMap(); if (!(map instanceof Serializable)) return;
1215          *
1216          * writeExternalFormToDisk((Serializable) map, getCanonicalFullCollectionName(map));
1217          */
1218 
1219         // test to make sure the canonical form has been preserved
1220         final Map<K, V> map = makeFullMap();
1221         if (map instanceof Serializable && !skipSerializedCanonicalTests() && isTestSerialization()) {
1222             @SuppressWarnings("unchecked")
1223             final Map<K, V> map2 = (Map<K, V>) readExternalFormFromDisk(getCanonicalFullCollectionName(map));
1224             assertEquals(getSampleKeys().length, map2.size(), "Map is the right size");
1225         }
1226     }
1227 
1228     /**
1229      * Tests that the {@link Map#keySet} collection is backed by the underlying map for clear().
1230      */
1231     @Test
1232     public void testKeySetClearChangesMap() {
1233         if (!isRemoveSupported()) {
1234             return;
1235         }
1236 
1237         // clear values, reflected in map
1238         resetFull();
1239         Set<K> keySet = getMap().keySet();
1240         assertFalse(getMap().isEmpty());
1241         assertFalse(keySet.isEmpty());
1242         keySet.clear();
1243         assertTrue(getMap().isEmpty());
1244         assertTrue(keySet.isEmpty());
1245 
1246         // clear map, reflected in values
1247         resetFull();
1248         keySet = getMap().keySet();
1249         assertFalse(getMap().isEmpty());
1250         assertFalse(keySet.isEmpty());
1251         getMap().clear();
1252         assertTrue(getMap().isEmpty());
1253         assertTrue(keySet.isEmpty());
1254     }
1255 
1256     /**
1257      * Verify that keySet.iterator.remove changes the underlying map.
1258      */
1259     @Test
1260     public void testKeySetIteratorRemoveChangesMap() {
1261         resetFull();
1262         for (final Iterator<K> iter = getMap().keySet().iterator(); iter.hasNext();) {
1263             final K key = iter.next();
1264             try {
1265                 iter.remove();
1266             } catch (final UnsupportedOperationException e) {
1267                 return;
1268             }
1269             assertFalse(getMap().containsKey(key));
1270         }
1271     }
1272 
1273     /**
1274      * Test keySet.removeAll.
1275      */
1276     @Test
1277     public void testKeySetRemoveAll() {
1278         resetFull();
1279         final Set<K> keys = getMap().keySet();
1280         final List<K> sampleKeysAsList = Arrays.asList(getSampleKeys());
1281         if (!keys.equals(sampleKeysAsList)) {
1282             return;
1283         }
1284         try {
1285             assertFalse(keys.removeAll(Collections.<K>emptySet()));
1286         } catch (final UnsupportedOperationException e) {
1287             return;
1288         }
1289         assertEquals(sampleKeysAsList, keys);
1290         try {
1291             assertTrue(keys.removeAll(sampleKeysAsList));
1292         } catch (final UnsupportedOperationException e) {
1293             return;
1294         }
1295         assertTrue(getMap().isEmpty());
1296     }
1297 
1298     /**
1299      * Tests that the {@link Map#keySet} set is backed by the underlying map by removing from the keySet set and testing if the key was removed from the map.
1300      */
1301     @Test
1302     public void testKeySetRemoveChangesMap() {
1303         resetFull();
1304         final K[] sampleKeys = getSampleKeys();
1305         final Set<K> keys = getMap().keySet();
1306         for (final K sampleKey : sampleKeys) {
1307             try {
1308                 keys.remove(sampleKey);
1309             } catch (final UnsupportedOperationException e) {
1310                 // if key.remove is unsupported, just skip this test
1311                 return;
1312             }
1313             assertFalse(getMap().containsKey(sampleKey), "Key should have been removed from the underlying map.");
1314         }
1315     }
1316 
1317     /**
1318      * Test keySet.retainAll.
1319      */
1320     @Test
1321     public void testKeySetRetainAll() {
1322         resetFull();
1323         final Set<K> keys = getMap().keySet();
1324         final List<K> sampleKeysAsList = Arrays.asList(getSampleKeys());
1325         if (!keys.equals(sampleKeysAsList)) {
1326             return;
1327         }
1328         try {
1329             assertFalse(keys.retainAll(sampleKeysAsList));
1330         } catch (final UnsupportedOperationException e) {
1331             return;
1332         }
1333         assertEquals(sampleKeysAsList, keys);
1334         try {
1335             assertTrue(keys.retainAll(Collections.<K>emptySet()));
1336         } catch (final UnsupportedOperationException e) {
1337             return;
1338         }
1339         assertTrue(getMap().isEmpty());
1340     }
1341 
1342     /**
1343      * Test to ensure that makeEmptyMap and makeFull returns a new non-null map with each invocation.
1344      */
1345     @Test
1346     public void testMakeMap() {
1347         final Map<K, V> em = makeObject();
1348         assertNotNull(em, "failure in test: makeEmptyMap must return a non-null map.");
1349 
1350         final Map<K, V> em2 = makeObject();
1351         assertNotNull(em, "failure in test: makeEmptyMap must return a non-null map.");
1352 
1353         assertNotSame(em, em2, "failure in test: makeEmptyMap must return a new map " + "with each invocation.");
1354 
1355         final Map<K, V> fm = makeFullMap();
1356         assertNotNull(fm, "failure in test: makeFullMap must return a non-null map.");
1357 
1358         final Map<K, V> fm2 = makeFullMap();
1359         assertNotNull(fm2, "failure in test: makeFullMap must return a non-null map.");
1360 
1361         assertNotSame(fm, fm2, "failure in test: makeFullMap must return a new map " + "with each invocation.");
1362     }
1363 
1364     /**
1365      * Tests {@link Map#clear()}. If the map {@link #isRemoveSupported() can add and remove elements}, then {@link Map#size()} and {@link Map#isEmpty()} are
1366      * used to ensure that map has no elements after a call to clear. If the map does not support adding and removing elements, this method checks to ensure
1367      * clear throws an UnsupportedOperationException.
1368      */
1369     @Test
1370     public void testMapClear() {
1371         if (!isRemoveSupported()) {
1372             resetFull();
1373             assertThrows(UnsupportedOperationException.class, () -> getMap().clear(), "Expected UnsupportedOperationException on clear");
1374             return;
1375         }
1376 
1377         resetEmpty();
1378         getMap().clear();
1379         getConfirmed().clear();
1380         verify();
1381 
1382         resetFull();
1383         getMap().clear();
1384         getConfirmed().clear();
1385         verify();
1386     }
1387 
1388     /**
1389      * Tests {@link Map#computeIfAbsent(Object, java.util.function.Function)}.
1390      *
1391      * @see Map#putIfAbsent(Object, Object)
1392      * @see #testMapPutIfAbsent()
1393      */
1394     @Test
1395     public void testMapComputeIfAbsent() {
1396         resetEmpty();
1397         final K[] keys = getSampleKeys();
1398         final V[] values = getSampleValues();
1399         final V[] newValues = getNewSampleValues();
1400         if (isPutAddSupported()) {
1401             for (final AtomicInteger inc = new AtomicInteger(); inc.get() < keys.length; inc.incrementAndGet()) {
1402                 final int i = inc.get();
1403                 final K key = keys[i];
1404                 final V value = values[i];
1405                 final boolean expectKey = key != null && value != null || key == null && !getMap().containsKey(key);
1406                 final Map<K, V> oldMap = new HashMap<>(getMap());
1407                 final Object currValue = computeIfAbsent(key, k -> value);
1408                 // map is updated if new value is not null
1409                 getConfirmed().computeIfAbsent(key, k -> value);
1410                 if (!isLazyMapTest()) {
1411                     // TODO LazyMap tests do not like this check
1412                     verify();
1413                 }
1414                 final Supplier<String> messageSupplier = () -> String.format("[%,d] map.computeIfAbsent key '%s', value '%s', old %s", inc.get(), key, value,
1415                         oldMap);
1416                 assertEquals(value, currValue, messageSupplier);
1417                 if (isLazyMapTest()) {
1418                     // TODO Is there a better way to write generic tests?
1419                     assertTrue(getMap().containsKey(key), messageSupplier);
1420                     assertTrue(getMap().containsValue(value), messageSupplier);
1421                 } else {
1422                     assertEquals(expectKey, getMap().containsKey(key), messageSupplier);
1423                     assertEquals(expectKey, getMap().containsValue(value), messageSupplier);
1424                 }
1425             }
1426             if (isPutChangeSupported()) {
1427                 // Piggyback on isPutChangeSupported() for computeIfAbsent with a caveat for null values.
1428                 for (final AtomicInteger inc = new AtomicInteger(); inc.get() < keys.length; inc.incrementAndGet()) {
1429                     final int i = inc.get();
1430                     final K key = keys[i];
1431                     final V value = newValues[i];
1432                     final boolean valueAlreadyPresent = getMap().containsValue(value);
1433                     final V prevValue = getMap().get(key);
1434                     final Map<K, V> oldMap = new HashMap<>(getMap());
1435                     final Object computedValue = computeIfAbsent(key, k -> value);
1436                     getConfirmed().computeIfAbsent(key, k -> value);
1437                     if (!isLazyMapTest()) {
1438                         // TODO LazyMap tests do not like this check
1439                         verify();
1440                     }
1441                     final V arrValue = values[i];
1442                     final Supplier<String> messageSupplier = () -> String.format("[%,d] map.computeIfAbsent key '%s', value '%s', old %s", inc.get(), key,
1443                             value, oldMap);
1444                     if (valueAlreadyPresent || key == null) {
1445                         assertNotEquals(value, computedValue, messageSupplier);
1446                     } else if (prevValue != null && value != null) {
1447                         assertEquals(prevValue, computedValue, messageSupplier);
1448                     } else if (prevValue == null) {
1449                         assertEquals(value, computedValue, messageSupplier);
1450                     } else if (value == null) {
1451                         assertEquals(prevValue, computedValue, messageSupplier);
1452                     }
1453                     if (prevValue == null) {
1454                         assertEquals(value, getMap().get(key), messageSupplier);
1455                     } else {
1456                         assertEquals(computedValue, getMap().get(key), messageSupplier);
1457                     }
1458                     assertTrue(getMap().containsKey(key), messageSupplier);
1459                     if (valueAlreadyPresent && value != null) {
1460                         // TODO The test fixture already contain a null value, so we condition this assertion
1461                         assertFalse(getMap().containsValue(value), messageSupplier);
1462                     }
1463                     // if duplicates are allowed, we're not guaranteed that the value
1464                     // no longer exists, so don't try checking that.
1465                     if (!isAllowDuplicateValues() && valueAlreadyPresent && value != null) {
1466                         assertFalse(getMap().containsValue(arrValue),
1467                                 String.format(
1468                                         "Map should not contain old value after computeIfAbsent when changed: [%,d] key '%s', prevValue '%s', newValue '%s'", i,
1469                                         key, prevValue, value));
1470                     }
1471                 }
1472             } else {
1473                 try {
1474                     // two possible exception here, either valid
1475                     computeIfAbsent(keys[0], k -> newValues[0]);
1476                     fail("Expected IllegalArgumentException or UnsupportedOperationException on putIfAbsent (change)");
1477                 } catch (final IllegalArgumentException | UnsupportedOperationException ex) {
1478                     // ignore
1479                 }
1480             }
1481         } else if (isPutChangeSupported()) {
1482             resetEmpty();
1483             try {
1484                 computeIfAbsent(keys[0], k -> values[0]);
1485                 fail("Expected UnsupportedOperationException or IllegalArgumentException on putIfAbsent (add) when fixed size");
1486             } catch (final IllegalArgumentException | UnsupportedOperationException ex) {
1487                 // ignore
1488             }
1489             resetFull();
1490             int i = 0;
1491             for (final Iterator<K> it = getMap().keySet().iterator(); it.hasNext() && i < newValues.length; i++) {
1492                 final K key = it.next();
1493                 final V newValue = newValues[i];
1494                 final boolean newValueAlready = getMap().containsValue(newValue);
1495                 final V prevValue = getMap().get(key);
1496                 final V oldValue = getMap().putIfAbsent(key, newValue);
1497                 final V value = getConfirmed().putIfAbsent(key, newValue);
1498                 verify();
1499                 assertEquals(value, oldValue, "Map.putIfAbsent should return previous value when changed");
1500                 assertEquals(prevValue, oldValue, "Map.putIfAbsent should return previous value when changed");
1501                 if (prevValue == null) {
1502                     assertEquals(newValue, getMap().get(key), String.format("[%,d] key '%s', prevValue '%s', newValue '%s'", i, key, prevValue, newValue));
1503                 } else {
1504                     assertEquals(oldValue, getMap().get(key), String.format("[%,d] key '%s', prevValue '%s', newValue '%s'", i, key, prevValue, newValue));
1505                 }
1506                 assertTrue(getMap().containsKey(key), "Map should still contain key after putIfAbsent when changed");
1507                 if (newValueAlready && newValue != null) {
1508                     // TODO The test fixture already contain a null value, so we condition this assertion
1509                     assertFalse(getMap().containsValue(newValue),
1510                             String.format("[%,d] Map at '%s' shouldn't contain new value '%s' after putIfAbsent when changed", i, key, newValue));
1511                 }
1512                 // if duplicates are allowed, we're not guaranteed that the value
1513                 // no longer exists, so don't try checking that.
1514                 if (!isAllowDuplicateValues()) {
1515                     assertFalse(getMap().containsValue(values[i]), "Map should not contain old value after putIfAbsent when changed");
1516                 }
1517             }
1518         } else {
1519             assertThrows(UnsupportedOperationException.class, () -> getMap().putIfAbsent(keys[0], values[0]),
1520                     "Expected UnsupportedOperationException on put (add)");
1521         }
1522     }
1523 
1524     /**
1525      * Tests {@link Map#computeIfPresent(Object, java.util.function.BiFunction)}.
1526      *
1527      * @see Map#computeIfPresent(Object, java.util.function.BiFunction)
1528      * @see #testMapComputeIfPresent()
1529      */
1530     @Test
1531     public void testMapComputeIfPresent() {
1532         resetEmpty();
1533         final K[] keys = getSampleKeys();
1534         final V[] values = getSampleValues();
1535         final V[] newValues = getNewSampleValues();
1536         if (isPutAddSupported()) {
1537             for (final AtomicInteger inc = new AtomicInteger(); inc.get() < keys.length; inc.incrementAndGet()) {
1538                 final int i = inc.get();
1539                 final K key = keys[i];
1540                 final V value = values[i];
1541                 final boolean expectKey = getMap().containsKey(key);
1542                 final Map<K, V> oldMap = new HashMap<>(getMap());
1543                 final Object computedValue = getMap().computeIfPresent(key, (k, v) -> value);
1544                 // map is updated if new or old value is not null
1545                 getConfirmed().computeIfPresent(key, (k, v) -> value);
1546                 if (!isLazyMapTest()) {
1547                     // TODO LazyMap tests do not like this check
1548                     verify();
1549                 }
1550                 final Supplier<String> messageSupplier = () -> String.format("[%,d] map.computeIfPresent key '%s', value '%s', old %s", inc.get(), key, value,
1551                         oldMap);
1552                 assertNull(computedValue, messageSupplier);
1553                 if (isLazyMapTest()) {
1554                     // TODO Is there a better way to write abstract tests?
1555                     assertTrue(getMap().containsKey(key), messageSupplier);
1556                     assertEquals(value == null, getMap().containsValue(value), messageSupplier);
1557                 } else {
1558                     assertEquals(expectKey, getMap().containsKey(key), messageSupplier);
1559                     assertEquals(expectKey, getMap().containsValue(value), messageSupplier);
1560                 }
1561                 // again, same result
1562                 getConfirmed().computeIfPresent(key, (k, v) -> value);
1563                 assertNull(computedValue, messageSupplier);
1564             }
1565             if (isPutChangeSupported()) {
1566                 // Piggyback on isPutChangeSupported() for computeIfAbsent with a caveat for null values.
1567                 for (final AtomicInteger inc = new AtomicInteger(); inc.get() < keys.length; inc.incrementAndGet()) {
1568                     final int i = inc.get();
1569                     final K key = keys[i];
1570                     final V value = values[i];
1571                     final V newValue = newValues[i];
1572                     final boolean valueAlreadyPresent = getMap().containsValue(newValue);
1573                     final V prevValue = getMap().get(key);
1574                     final Map<K, V> oldMap = new HashMap<>(getMap());
1575                     final Supplier<String> messageSupplier0 = () -> String.format("[%,d] map.computeIfPresent key '%s', value '%s', old %s", inc.get(), key,
1576                             value, oldMap);
1577                     final Supplier<String> messageSupplier1 = () -> String.format("[%,d] map.computeIfPresent key '%s', newValue '%s', old %s", inc.get(), key,
1578                             newValue, oldMap);
1579                     // compute same
1580                     oldMap.clear();
1581                     oldMap.putAll(getMap());
1582                     getMap().put(key, value);
1583                     getConfirmed().put(key, value);
1584                     V computedValue = getMap().computeIfPresent(key, (k, v) -> value);
1585                     getConfirmed().computeIfPresent(key, (k, v) -> value);
1586                     if (value != null) {
1587                         assertNotNull(computedValue, messageSupplier0);
1588                     } else {
1589                         assertNull(computedValue, messageSupplier0);
1590                     }
1591                     // compute new (mod null)
1592                     oldMap.clear();
1593                     oldMap.putAll(getMap());
1594                     final boolean keyPresent = getMap().containsKey(key);
1595                     computedValue = getMap().computeIfPresent(key, (k, v) -> newValue);
1596                     getConfirmed().computeIfPresent(key, (k, v) -> newValue);
1597                     if (!isLazyMapTest()) {
1598                         // TODO LazyMap tests do not like this check
1599                         verify();
1600                     }
1601                     if (keyPresent && value != null) {
1602                         assertEquals(newValue, computedValue, messageSupplier1);
1603                     } else {
1604                         assertNull(computedValue, messageSupplier1);
1605                     }
1606                     assertEquals(newValue != null, getMap().containsKey(key), messageSupplier1);
1607                     // if duplicates are allowed, we're not guaranteed that the value
1608                     // no longer exists, so don't try checking that.
1609                     if (!isAllowDuplicateValues() && valueAlreadyPresent && newValue != null) {
1610                         assertFalse(getMap().containsValue(value),
1611                                 String.format(
1612                                         "Map should not contain old value after computeIfPresent when changed: [%,d] key '%s', prevValue '%s', newValue '%s'",
1613                                         i, key, prevValue, newValue));
1614                     }
1615                 }
1616             } else {
1617                 try {
1618                     // two possible exception here, either valid
1619                     getMap().computeIfPresent(keys[0], (k, v) -> newValues[0]);
1620                     fail("Expected IllegalArgumentException or UnsupportedOperationException on putIfAbsent (change)");
1621                 } catch (final IllegalArgumentException | UnsupportedOperationException ex) {
1622                     // ignore
1623                 }
1624             }
1625         } else if (isPutChangeSupported()) {
1626             // compute if present is a put.
1627             resetEmpty();
1628             getMap().computeIfPresent(keys[0], (k, v) -> values[0]);
1629             if (isPutAddSupported()) {
1630                 resetFull();
1631                 int i = 0;
1632                 for (final Iterator<K> it = getMap().keySet().iterator(); it.hasNext() && i < newValues.length; i++) {
1633                     final K key = it.next();
1634                     final V newValue = newValues[i];
1635                     final boolean newValueAlready = getMap().containsValue(newValue);
1636                     final V prevValue = getMap().get(key);
1637                     final V oldValue = getMap().computeIfPresent(key, (k, v) -> newValue);
1638                     final V value = getConfirmed().computeIfPresent(key, (k, v) -> newValue);
1639                     verify();
1640                     assertEquals(value, oldValue, "Map.putIfAbsent should return previous value when changed");
1641                     assertEquals(prevValue, oldValue, "Map.putIfAbsent should return previous value when changed");
1642                     if (prevValue == null) {
1643                         assertEquals(newValue, getMap().get(key), String.format("[%,d] key '%s', prevValue '%s', newValue '%s'", i, key, prevValue, newValue));
1644                     } else {
1645                         assertEquals(oldValue, getMap().get(key), String.format("[%,d] key '%s', prevValue '%s', newValue '%s'", i, key, prevValue, newValue));
1646                     }
1647                     assertTrue(getMap().containsKey(key), "Map should still contain key after putIfAbsent when changed");
1648                     if (newValueAlready && newValue != null) {
1649                         // TODO The test fixture already contain a null value, so we condition this assertion
1650                         assertFalse(getMap().containsValue(newValue),
1651                                 String.format("[%,d] Map at '%s' shouldn't contain new value '%s' after putIfAbsent when changed", i, key, newValue));
1652                     }
1653                     // if duplicates are allowed, we're not guaranteed that the value
1654                     // no longer exists, so don't try checking that.
1655                     if (!isAllowDuplicateValues()) {
1656                         assertFalse(getMap().containsValue(values[i]), "Map should not contain old value after putIfAbsent when changed");
1657                     }
1658                 }
1659             }
1660         } else if (getMap().containsKey(keys[0])) {
1661             assertThrows(UnsupportedOperationException.class, () -> getMap().computeIfPresent(keys[0], (k, v) -> values[0]),
1662                     "Expected UnsupportedOperationException on put (add)");
1663         } else {
1664             // doesn't throw
1665             getMap().computeIfPresent(keys[0], (k, v) -> values[0]);
1666         }
1667     }
1668 
1669     @Test
1670     public void testMapComputeIfPresentOnEmpty() {
1671         resetEmpty();
1672         assertTrue(getMap().isEmpty());
1673         final K[] otherKeys = getOtherKeys();
1674         final V[] otherValues = getOtherValues();
1675         final K key = otherKeys[0];
1676         assertFalse(getMap().containsKey(key));
1677         assertNull(getMap().computeIfPresent(key, (k, v) -> otherValues[0]));
1678         assertEquals(isLazyMapTest(), getMap().containsKey(key));
1679         assertEquals(!isLazyMapTest(), getMap().isEmpty());
1680     }
1681 
1682     /**
1683      * Tests Map.containsKey(Object) by verifying it returns false for all sample keys on a map created using an empty map and returns true for all sample keys
1684      * returned on a full map.
1685      */
1686     @Test
1687     public void testMapContainsKey() {
1688         final Object[] keys = getSampleKeys();
1689 
1690         resetEmpty();
1691         for (final Object key : keys) {
1692             assertFalse(getMap().containsKey(key), "Map must not contain key when map is empty");
1693         }
1694         verify();
1695 
1696         resetFull();
1697         for (final Object key : keys) {
1698             assertTrue(getMap().containsKey(key), "Map must contain key for a mapping in the map. " + "Missing: " + key);
1699         }
1700         verify();
1701     }
1702 
1703     /**
1704      * Tests Map.containsValue(Object) by verifying it returns false for all sample values on an empty map and returns true for all sample values on a full map.
1705      */
1706     @Test
1707     public void testMapContainsValue() {
1708         final Object[] values = getSampleValues();
1709         resetEmpty();
1710         if (isAllowNullValueGet()) {
1711             assertFalse(getMap().containsValue(null));
1712         } else {
1713             assertThrows(NullPointerException.class, () -> getMap().containsValue(null));
1714         }
1715         for (final Object value : values) {
1716             assertFalse(getMap().containsValue(value), "Empty map must not contain value");
1717         }
1718         verify();
1719 
1720         resetFull();
1721         for (final Object value : values) {
1722             assertTrue(getMap().containsValue(value), "Map must contain value for a mapping in the map.");
1723         }
1724         verify();
1725     }
1726 
1727     /**
1728      * Tests Map.equals(Object)
1729      */
1730     @Test
1731     public void testMapEquals() {
1732         resetEmpty();
1733         assertEquals(getMap(), confirmed, "Empty maps unequal.");
1734         verify();
1735 
1736         resetFull();
1737         assertEquals(getMap(), confirmed, "Full maps unequal.");
1738         verify();
1739 
1740         resetFull();
1741         // modify the HashMap created from the full map and make sure this
1742         // change results in map.equals() to return false.
1743         final Iterator<K> iter = confirmed.keySet().iterator();
1744         iter.next();
1745         iter.remove();
1746         assertFalse(getMap().equals(confirmed), "Different maps equal.");
1747 
1748         resetFull();
1749         assertFalse(getMap().equals(null), "equals(null) returned true.");
1750         assertFalse(getMap().equals(new Object()), "equals(new Object()) returned true.");
1751         verify();
1752     }
1753 
1754     /**
1755      * Tests Map.get(Object)
1756      */
1757     @Test
1758     public void testMapGet() {
1759         resetEmpty();
1760 
1761         final Object[] keys = getSampleKeys();
1762         final Object[] values = getSampleValues();
1763 
1764         for (final Object key : keys) {
1765             assertNull(getMap().get(key), "Empty map.get() should return null.");
1766         }
1767         verify();
1768 
1769         resetFull();
1770         for (int i = 0; i < keys.length; i++) {
1771             assertEquals(values[i], getMap().get(keys[i]), "Full map.get() should return value from mapping.");
1772         }
1773     }
1774 
1775     /**
1776      * Tests {@link Map#getOrDefault(Object, Object)}.
1777      */
1778     @Test
1779     public void testMapGetOrDefault() {
1780         resetEmpty();
1781         final K[] keys = getSampleKeys();
1782         final V[] values = getSampleValues();
1783         for (final K key : keys) {
1784             assertNull(getMap().getOrDefault(key, null));
1785         }
1786         final K[] otherKeys = getOtherKeys();
1787         final V[] otherValues = getOtherValues();
1788         for (int i = 0; i < otherKeys.length; i++) {
1789             final K otherKey = otherKeys[i];
1790             assertNull(getMap().getOrDefault(otherKey, null));
1791             final V otherValue = otherValues[i];
1792             if (getMap().containsKey(otherKey)) {
1793                 assertEquals(getMap().get(otherKey), getMap().getOrDefault(otherKey, otherValue));
1794             } else {
1795                 assertEquals(otherValue, getMap().getOrDefault(otherKey, otherValue));
1796             }
1797         }
1798         if (!isLazyMapTest()) {
1799             // TODO LazyMap tests do not like this check
1800             verify();
1801         }
1802         resetFull();
1803         for (int i = 0; i < keys.length; i++) {
1804             assertEquals(values[i], getMap().getOrDefault(keys[i], values[i]));
1805         }
1806     }
1807 
1808     /**
1809      * Tests Map.hashCode()
1810      */
1811     @Test
1812     public void testMapHashCode() {
1813         resetEmpty();
1814         assertEquals(getMap().hashCode(), confirmed.hashCode(), "Empty maps have different hashCodes.");
1815 
1816         resetFull();
1817         assertEquals(getMap().hashCode(), confirmed.hashCode(), "Equal maps have different hashCodes.");
1818     }
1819 
1820     /**
1821      * Tests Map.isEmpty()
1822      */
1823     @Test
1824     public void testMapIsEmpty() {
1825         resetEmpty();
1826         assertTrue(getMap().isEmpty(), "Map.isEmpty() should return true with an empty map");
1827         verify();
1828 
1829         resetFull();
1830         assertFalse(getMap().isEmpty(), "Map.isEmpty() should return false with a non-empty map");
1831         verify();
1832     }
1833 
1834     /**
1835      * Tests Map.put(Object, Object)
1836      */
1837     @Test
1838     public void testMapPut() {
1839         resetEmpty();
1840         final K[] keys = getSampleKeys();
1841         final V[] values = getSampleValues();
1842         final V[] newValues = getNewSampleValues();
1843 
1844         if (isPutAddSupported()) {
1845             for (int i = 0; i < keys.length; i++) {
1846                 final Object o = getMap().put(keys[i], values[i]);
1847                 getConfirmed().put(keys[i], values[i]);
1848                 verify();
1849                 assertNull(o, "First map.put should return null");
1850                 assertTrue(getMap().containsKey(keys[i]), "Map should contain key after put");
1851                 assertTrue(getMap().containsValue(values[i]), "Map should contain value after put");
1852             }
1853             if (isPutChangeSupported()) {
1854                 for (int i = 0; i < keys.length; i++) {
1855                     final Object o = getMap().put(keys[i], newValues[i]);
1856                     getConfirmed().put(keys[i], newValues[i]);
1857                     verify();
1858                     assertEquals(values[i], o, "Map.put should return previous value when changed");
1859                     assertTrue(getMap().containsKey(keys[i]), "Map should still contain key after put when changed");
1860                     assertTrue(getMap().containsValue(newValues[i]), "Map should contain new value after put when changed");
1861 
1862                     // if duplicates are allowed, we're not guaranteed that the value
1863                     // no longer exists, so don't try checking that.
1864                     if (!isAllowDuplicateValues()) {
1865                         assertFalse(getMap().containsValue(values[i]), "Map should not contain old value after put when changed");
1866                     }
1867                 }
1868             } else {
1869                 try {
1870                     // two possible exception here, either valid
1871                     getMap().put(keys[0], newValues[0]);
1872                     fail("Expected IllegalArgumentException or UnsupportedOperationException on put (change)");
1873                 } catch (final IllegalArgumentException | UnsupportedOperationException ex) {
1874                     // ignore
1875                 }
1876             }
1877 
1878         } else if (isPutChangeSupported()) {
1879             resetEmpty();
1880             try {
1881                 getMap().put(keys[0], values[0]);
1882                 fail("Expected UnsupportedOperationException or IllegalArgumentException on put (add) when fixed size");
1883             } catch (final IllegalArgumentException | UnsupportedOperationException ex) {
1884                 // ignore
1885             }
1886 
1887             resetFull();
1888             int i = 0;
1889             for (final Iterator<K> it = getMap().keySet().iterator(); it.hasNext() && i < newValues.length; i++) {
1890                 final K key = it.next();
1891                 final V o = getMap().put(key, newValues[i]);
1892                 final V value = getConfirmed().put(key, newValues[i]);
1893                 verify();
1894                 assertEquals(value, o, "Map.put should return previous value when changed");
1895                 assertTrue(getMap().containsKey(key), "Map should still contain key after put when changed");
1896                 assertTrue(getMap().containsValue(newValues[i]), "Map should contain new value after put when changed");
1897 
1898                 // if duplicates are allowed, we're not guaranteed that the value
1899                 // no longer exists, so don't try checking that.
1900                 if (!isAllowDuplicateValues()) {
1901                     assertFalse(getMap().containsValue(values[i]), "Map should not contain old value after put when changed");
1902                 }
1903             }
1904         } else {
1905             assertThrows(UnsupportedOperationException.class, () -> getMap().put(keys[0], values[0]), "Expected UnsupportedOperationException on put (add)");
1906         }
1907     }
1908 
1909     /**
1910      * Tests Map.putAll(map)
1911      */
1912     @Test
1913     public void testMapPutAll() {
1914         if (!isPutAddSupported()) {
1915             if (!isPutChangeSupported()) {
1916                 final Map<K, V> temp = makeFullMap();
1917                 resetEmpty();
1918                 assertThrows(UnsupportedOperationException.class, () -> getMap().putAll(temp), "Expected UnsupportedOperationException on putAll");
1919             }
1920             return;
1921         }
1922 
1923         // check putAll OK adding empty map to empty map
1924         resetEmpty();
1925         assertEquals(0, getMap().size());
1926         getMap().putAll(new HashMap<>());
1927         assertEquals(0, getMap().size());
1928 
1929         // check putAll OK adding empty map to non-empty map
1930         resetFull();
1931         final int size = getMap().size();
1932         getMap().putAll(new HashMap<>());
1933         assertEquals(size, getMap().size());
1934 
1935         // check putAll OK adding non-empty map to empty map
1936         resetEmpty();
1937         Map<K, V> m2 = makeFullMap();
1938         getMap().putAll(m2);
1939         getConfirmed().putAll(m2);
1940         verify();
1941 
1942         // check putAll OK adding non-empty JDK map to empty map
1943         resetEmpty();
1944         m2 = makeConfirmedMap();
1945         final K[] keys = getSampleKeys();
1946         final V[] values = getSampleValues();
1947         for (int i = 0; i < keys.length; i++) {
1948             m2.put(keys[i], values[i]);
1949         }
1950         getMap().putAll(m2);
1951         getConfirmed().putAll(m2);
1952         verify();
1953 
1954         // check putAll OK adding non-empty JDK map to non-empty map
1955         resetEmpty();
1956         m2 = makeConfirmedMap();
1957         getMap().put(keys[0], values[0]);
1958         getConfirmed().put(keys[0], values[0]);
1959         verify();
1960         for (int i = 1; i < keys.length; i++) {
1961             m2.put(keys[i], values[i]);
1962         }
1963         getMap().putAll(m2);
1964         getConfirmed().putAll(m2);
1965         verify();
1966     }
1967 
1968     /**
1969      * Tests {@link Map#putIfAbsent(Object, Object)}.
1970      *
1971      * @see Map#computeIfAbsent(Object, java.util.function.Function)
1972      */
1973     @Test
1974     public void testMapPutIfAbsent() {
1975         resetEmpty();
1976         final K[] keys = getSampleKeys();
1977         final V[] values = getSampleValues();
1978         final V[] newValues = getNewSampleValues();
1979         if (isPutAddSupported()) {
1980             for (int i = 0; i < keys.length; i++) {
1981                 final K key = keys[i];
1982                 final V value = values[i];
1983                 final Object o = putIfAbsent(key, value);
1984                 getConfirmed().putIfAbsent(key, value);
1985                 verify();
1986                 assertNull(o, "First map.putIfAbsent should return null");
1987                 assertTrue(getMap().containsKey(key), "Map should contain key after putIfAbsent");
1988                 assertTrue(getMap().containsValue(value), "Map should contain value after putIfAbsent");
1989             }
1990             if (isPutChangeSupported()) {
1991                 // Piggyback on isPutChangeSupported() for putIfAbsent with a caveat for null values.
1992                 for (int i = 0; i < keys.length; i++) {
1993                     final K key = keys[i];
1994                     final V newValue = newValues[i];
1995                     final boolean newValueAlready = getMap().containsValue(newValue);
1996                     final V prevValue = getMap().get(key);
1997                     final Object oldValue = putIfAbsent(key, newValue);
1998                     getConfirmed().putIfAbsent(key, newValue);
1999                     verify();
2000                     final V arrValue = values[i];
2001                     assertEquals(arrValue, oldValue, "Map.putIfAbsent should return previous value when changed");
2002                     assertEquals(prevValue, oldValue, "Map.putIfAbsent should return previous value when changed");
2003                     if (prevValue == null) {
2004                         assertEquals(newValue, getMap().get(key), String.format("[%,d] key '%s', prevValue '%s', newValue '%s'", i, key, prevValue, newValue));
2005                     } else {
2006                         assertEquals(oldValue, getMap().get(key), String.format("[%,d] key '%s', prevValue '%s', newValue '%s'", i, key, prevValue, newValue));
2007                     }
2008                     assertTrue(getMap().containsKey(key), "Map should still contain key after putIfAbsent when changed");
2009                     if (newValueAlready && newValue != null) {
2010                         // TODO The test fixture already contain a null value, so we condition this assertion
2011                         assertFalse(getMap().containsValue(newValue),
2012                                 String.format("[%,d] Map at '%s' shouldn't contain new value '%s' after putIfAbsent when changed", i, key, newValue));
2013                     }
2014                     // if duplicates are allowed, we're not guaranteed that the value
2015                     // no longer exists, so don't try checking that.
2016                     if (!isAllowDuplicateValues() && newValueAlready && newValue != null) {
2017                         assertFalse(getMap().containsValue(arrValue),
2018                                 String.format("Map should not contain old value after putIfAbsent when changed: [%,d] key '%s', prevValue '%s', newValue '%s'",
2019                                         i, key, prevValue, newValue));
2020                     }
2021                 }
2022             } else {
2023                 try {
2024                     // two possible exception here, either valid
2025                     putIfAbsent(keys[0], newValues[0]);
2026                     fail("Expected IllegalArgumentException or UnsupportedOperationException on putIfAbsent (change)");
2027                 } catch (final IllegalArgumentException | UnsupportedOperationException ex) {
2028                     // ignore
2029                 }
2030             }
2031         } else if (isPutChangeSupported()) {
2032             resetEmpty();
2033             final K key0 = keys[0];
2034             final V value0 = values[0];
2035             if (getMap().containsKey(key0)) {
2036                 // don't throw
2037                 assertEquals(getMap().get(key0), putIfAbsent(key0, value0));
2038             } else if (isPutAddSupported()) {
2039                 putIfAbsent(key0, value0);
2040             } else {
2041                 // throw (fixed size for example)
2042                 try {
2043                     putIfAbsent(key0, value0);
2044                     fail("Expected UnsupportedOperationException or IllegalArgumentException on putIfAbsent (add) when fixed size");
2045                 } catch (final IllegalArgumentException | UnsupportedOperationException ex) {
2046                     // ignore
2047                 }
2048             }
2049             resetFull();
2050             int i = 0;
2051             for (final Iterator<K> it = getMap().keySet().iterator(); it.hasNext() && i < newValues.length; i++) {
2052                 final K key = it.next();
2053                 final V newValue = newValues[i];
2054                 final boolean newValueAlready = getMap().containsValue(newValue);
2055                 final V prevValue = getMap().get(key);
2056                 final V oldValue = putIfAbsent(key, newValue);
2057                 final V value = getConfirmed().putIfAbsent(key, newValue);
2058                 verify();
2059                 assertEquals(value, oldValue, "Map.putIfAbsent should return previous value when changed");
2060                 assertEquals(prevValue, oldValue, "Map.putIfAbsent should return previous value when changed");
2061                 if (prevValue == null) {
2062                     assertEquals(newValue, getMap().get(key), String.format("[%,d] key '%s', prevValue '%s', newValue '%s'", i, key, prevValue, newValue));
2063                 } else {
2064                     assertEquals(oldValue, getMap().get(key), String.format("[%,d] key '%s', prevValue '%s', newValue '%s'", i, key, prevValue, newValue));
2065                 }
2066                 assertTrue(getMap().containsKey(key), "Map should still contain key after putIfAbsent when changed");
2067                 if (newValueAlready && newValue != null) {
2068                     // TODO The test fixture already contain a null value, so we condition this assertion
2069                     assertFalse(getMap().containsValue(newValue),
2070                             String.format("[%,d] Map at '%s' shouldn't contain new value '%s' after putIfAbsent when changed", i, key, newValue));
2071                 }
2072                 // if duplicates are allowed, we're not guaranteed that the value
2073                 // no longer exists, so don't try checking that.
2074                 if (!isAllowDuplicateValues()) {
2075                     assertFalse(getMap().containsValue(values[i]), "Map should not contain old value after putIfAbsent when changed");
2076                 }
2077             }
2078         } else {
2079             assertThrows(UnsupportedOperationException.class, () -> putIfAbsent(keys[0], values[0]), "Expected UnsupportedOperationException on put (add)");
2080         }
2081     }
2082 
2083     /**
2084      * Tests Map.put(null, value)
2085      */
2086     @Test
2087     public void testMapPutNullKey() {
2088         resetFull();
2089         final V[] values = getSampleValues();
2090 
2091         if (isPutAddSupported()) {
2092             if (isAllowNullKey()) {
2093                 getMap().put(null, values[0]);
2094             } else {
2095                 try {
2096                     getMap().put(null, values[0]);
2097                     fail("put(null, value) should throw NPE/IAE");
2098                 } catch (final NullPointerException | IllegalArgumentException ex) {
2099                 }
2100             }
2101         }
2102     }
2103 
2104     /**
2105      * Tests Map.put(null, value)
2106      */
2107     @Test
2108     public void testMapPutNullValue() {
2109         resetFull();
2110         final K[] keys = getSampleKeys();
2111 
2112         if (isPutAddSupported()) {
2113             if (isAllowNullValue()) {
2114                 getMap().put(keys[0], null);
2115             } else {
2116                 try {
2117                     getMap().put(keys[0], null);
2118                     fail("put(key, null) should throw NPE/IAE");
2119                 } catch (final NullPointerException | IllegalArgumentException ex) {
2120                 }
2121             }
2122         }
2123     }
2124 
2125     /**
2126      * Tests Map.remove(Object)
2127      */
2128     @Test
2129     public void testMapRemove() {
2130         if (!isRemoveSupported()) {
2131             resetFull();
2132             assertThrows(UnsupportedOperationException.class, () -> getMap().remove(getMap().keySet().iterator().next()),
2133                     "Expected UnsupportedOperationException on remove");
2134             return;
2135         }
2136 
2137         resetEmpty();
2138 
2139         final Object[] keys = getSampleKeys();
2140         final Object[] values = getSampleValues();
2141         for (final Object key : keys) {
2142             final Object o = getMap().remove(key);
2143             assertNull(o, "First map.remove should return null");
2144         }
2145         verify();
2146 
2147         resetFull();
2148 
2149         for (int i = 0; i < keys.length; i++) {
2150             final Object o = getMap().remove(keys[i]);
2151             getConfirmed().remove(keys[i]);
2152             verify();
2153 
2154             assertEquals(values[i], o, "map.remove with valid key should return value");
2155         }
2156 
2157         final Object[] other = getOtherKeys();
2158 
2159         resetFull();
2160         final int size = getMap().size();
2161         for (final Object element : other) {
2162             final Object o = getMap().remove(element);
2163             assertNull(o, "map.remove for nonexistent key should return null");
2164             assertEquals(size, getMap().size(), "map.remove for nonexistent key should not " + "shrink map");
2165         }
2166         verify();
2167     }
2168 
2169     /**
2170      * Tests Map.size()
2171      */
2172     @Test
2173     public void testMapSize() {
2174         resetEmpty();
2175         assertEquals(0, getMap().size(), "Map.size() should be 0 with an empty map");
2176         verify();
2177 
2178         resetFull();
2179         assertEquals(getSampleKeys().length, getMap().size(), "Map.size() should equal the number of entries " + "in the map");
2180         verify();
2181     }
2182 
2183     /**
2184      * Tests Map.toString(). Since the format of the string returned by the toString() method is not defined in the Map interface, there is no common way to
2185      * test the results of the toString() method. Therefore, it is encouraged that Map implementations override this test with one that checks the format
2186      * matches any format defined in its API. This default implementation just verifies that the toString() method does not return null.
2187      */
2188     @Test
2189     public void testMapToString() {
2190         resetEmpty();
2191         assertNotNull(getMap().toString(), "Empty map toString() should not return null");
2192         verify();
2193 
2194         resetFull();
2195         assertNotNull(getMap().toString(), "Empty map toString() should not return null");
2196         verify();
2197     }
2198 
2199     /**
2200      * Tests {@link Map#remove(Object, Object)}.
2201      */
2202     @Test
2203     public void testRemoveKeyValue() {
2204         assumeTrue(isRemoveSupported());
2205         resetFull();
2206         final K[] sampleKeys = getSampleKeys();
2207         final V[] sampleValues = getSampleValues();
2208         assertFalse(getMap().isEmpty());
2209         for (int i = 0; i < sampleKeys.length; i++) {
2210             assertTrue(getMap().remove(sampleKeys[i], sampleValues[i]));
2211         }
2212         assertTrue(getMap().isEmpty());
2213     }
2214 
2215     /**
2216      * Tests {@link Map#replaceAll(java.util.function.BiFunction)}.
2217      */
2218     @Test
2219     public void testReplaceAll() {
2220         assumeTrue(isSetValueSupported() && isReplaceAllSupported());
2221         resetFull();
2222         final V[] newValues = getNewSampleValues();
2223         assertFalse(getMap().isEmpty());
2224         getMap().replaceAll((k, v) -> v);
2225         assertEquals(makeFullMap(), getMap());
2226         getMap().replaceAll((k, v) -> v);
2227         final AtomicInteger i = new AtomicInteger();
2228         final Map<K, V> newMap = new HashMap<>();
2229         getMap().replaceAll((k, v) -> {
2230             final V v2 = newValues[i.getAndIncrement()];
2231             newMap.put(k, v2);
2232             return v2;
2233         });
2234         assertEquals(newMap, getMap());
2235     }
2236 
2237     /**
2238      * Tests {@link Map#replace(Object, Object)}.
2239      */
2240     @Test
2241     public void testReplaceKeyValue() {
2242         assumeTrue(isRemoveSupported());
2243         resetFull();
2244         final K[] sampleKeys = getSampleKeys();
2245         final V[] sampleValues = getSampleValues();
2246         final V[] newValues = getNewSampleValues();
2247         assertFalse(getMap().isEmpty());
2248         for (final AtomicInteger inc = new AtomicInteger(); inc.get() < sampleKeys.length; inc.incrementAndGet()) {
2249             final int i = inc.get();
2250             final V value = sampleValues[i];
2251             final K sampleKey = sampleKeys[i];
2252             final Supplier<String> messageSupplier = () -> String.format("[%,d] key '%s'; %s", inc.get(), sampleKey, getMap());
2253             assertEquals(value, getMap().replace(sampleKey, value), messageSupplier);
2254             assertEquals(value, getMap().replace(sampleKey, value), messageSupplier);
2255             final V newValue = newValues[i];
2256             assertEquals(value, getMap().replace(sampleKey, newValue), messageSupplier);
2257             V oldValue = getMap().get(sampleKey);
2258             assertEquals(oldValue, getMap().replace(sampleKey, newValue), messageSupplier);
2259             oldValue = getMap().get(sampleKey);
2260             assertEquals(oldValue, getMap().replace(sampleKey, value), messageSupplier);
2261             if (isAllowNullValue()) {
2262                 oldValue = getMap().get(sampleKey);
2263                 assertEquals(oldValue, getMap().replace(sampleKey, null), messageSupplier);
2264                 assertNull(getMap().get(sampleKey), messageSupplier);
2265                 assertNull(getMap().replace(sampleKey, null), messageSupplier);
2266                 assertNull(getMap().replace(sampleKey, value), messageSupplier);
2267             }
2268         }
2269     }
2270 
2271     /**
2272      * Tests {@link Map#replace(Object, Object, Object)}.
2273      */
2274     @Test
2275     public void testReplaceKeyValueValue() {
2276         assumeTrue(isRemoveSupported());
2277         resetFull();
2278         final K[] sampleKeys = getSampleKeys();
2279         final V[] sampleValues = getSampleValues();
2280         final V[] newValues = getNewSampleValues();
2281         assertFalse(getMap().isEmpty());
2282         for (final AtomicInteger inc = new AtomicInteger(); inc.get() < sampleKeys.length; inc.incrementAndGet()) {
2283             final int i = inc.get();
2284             final V value = sampleValues[i];
2285             final K key = sampleKeys[i];
2286             final Supplier<String> messageSupplier = () -> String.format("[%,d] key '%s', value '%s'; %s", inc.get(), key, value, getMap());
2287             boolean containsKey = getMap().containsKey(key);
2288             assertEquals(containsKey, getMap().replace(key, value, value), messageSupplier);
2289             containsKey = getMap().containsKey(key);
2290             assertEquals(containsKey, getMap().replace(key, value, value), messageSupplier);
2291             final V newValue = newValues[i];
2292             containsKey = getMap().containsKey(key);
2293             assertEquals(containsKey, getMap().replace(key, value, newValue), messageSupplier);
2294             V oldValue = getMap().get(key);
2295             containsKey = getMap().containsKey(key);
2296             assertEquals(containsKey, getMap().replace(key, oldValue, newValue), messageSupplier);
2297             oldValue = getMap().get(key);
2298             containsKey = getMap().containsKey(key);
2299             assertEquals(containsKey, getMap().replace(key, oldValue, value), messageSupplier);
2300             if (isAllowNullValue()) {
2301                 oldValue = getMap().get(key);
2302                 containsKey = getMap().containsKey(key);
2303                 assertEquals(containsKey, getMap().replace(key, oldValue, null), messageSupplier);
2304                 assertNull(getMap().get(key), messageSupplier);
2305                 containsKey = getMap().containsKey(key);
2306                 assertEquals(containsKey, getMap().replace(key, null, null), messageSupplier);
2307                 containsKey = getMap().containsKey(key);
2308                 assertEquals(containsKey, getMap().replace(key, null, value), messageSupplier);
2309             }
2310         }
2311     }
2312 
2313     /**
2314      * Test to ensure the test setup is working properly. This method checks to ensure that the getSampleKeys and getSampleValues methods are returning results
2315      * that look appropriate. That is, they both return a non-null array of equal length. The keys array must not have any duplicate values, and may only
2316      * contain a (single) null key if isNullKeySupported() returns true. The values array must only have a null value if useNullValue() is true and may only
2317      * have duplicate values if isAllowDuplicateValues() returns true.
2318      */
2319     @Test
2320     public void testSampleMappings() {
2321         final Object[] keys = getSampleKeys();
2322         final Object[] values = getSampleValues();
2323         final Object[] newValues = getNewSampleValues();
2324 
2325         assertNotNull(keys, "failure in test: Must have keys returned from " + "getSampleKeys.");
2326 
2327         assertNotNull(values, "failure in test: Must have values returned from " + "getSampleValues.");
2328 
2329         // verify keys and values have equivalent lengths (in case getSampleX are
2330         // overridden)
2331         assertEquals(keys.length, values.length, "failure in test: not the same number of sample " + "keys and values.");
2332 
2333         assertEquals(values.length, newValues.length, "failure in test: not the same number of values and new values.");
2334 
2335         // verify there aren't duplicate keys, and check values
2336         for (int i = 0; i < keys.length - 1; i++) {
2337             for (int j = i + 1; j < keys.length; j++) {
2338                 assertTrue(keys[i] != null || keys[j] != null, "failure in test: duplicate null keys.");
2339                 assertTrue(keys[i] == null || keys[j] == null || !keys[i].equals(keys[j]) && !keys[j].equals(keys[i]),
2340                         "failure in test: duplicate non-null key.");
2341             }
2342             assertTrue(keys[i] != null || isAllowNullKey(), "failure in test: found null key, but isNullKeySupported " + "is false.");
2343             assertTrue(values[i] != null || isAllowNullValue(), "failure in test: found null value, but isNullValueSupported " + "is false.");
2344             assertTrue(newValues[i] != null || isAllowNullValue(), "failure in test: found null new value, but isNullValueSupported " + "is false.");
2345             assertTrue(values[i] != newValues[i] && (values[i] == null || !values[i].equals(newValues[i])),
2346                     "failure in test: values should not be the same as new value");
2347         }
2348     }
2349 
2350     /**
2351      * Tests that the {@link Map#bitMaps} collection is backed by the underlying map for clear().
2352      */
2353     @Test
2354     public void testValuesClearChangesMap() {
2355         if (!isRemoveSupported()) {
2356             return;
2357         }
2358 
2359         // clear values, reflected in map
2360         resetFull();
2361         Collection<V> values = getMap().values();
2362         assertFalse(getMap().isEmpty());
2363         assertFalse(values.isEmpty());
2364         values.clear();
2365         assertTrue(getMap().isEmpty());
2366         assertTrue(values.isEmpty());
2367 
2368         // clear map, reflected in values
2369         resetFull();
2370         values = getMap().values();
2371         assertFalse(getMap().isEmpty());
2372         assertFalse(values.isEmpty());
2373         getMap().clear();
2374         assertTrue(getMap().isEmpty());
2375         assertTrue(values.isEmpty());
2376     }
2377 
2378     /**
2379      * Verifies that values.iterator.remove changes the underlying map.
2380      */
2381     @Test
2382     @SuppressWarnings("boxing") // OK in test code
2383     public void testValuesIteratorRemoveChangesMap() {
2384         resetFull();
2385         final List<V> sampleValuesAsList = Arrays.asList(getSampleValues());
2386         final Map<V, Integer> cardinality = CollectionUtils.getCardinalityMap(sampleValuesAsList);
2387         final Collection<V> values = getMap().values();
2388         for (final Iterator<V> iter = values.iterator(); iter.hasNext();) {
2389             final V value = iter.next();
2390             Integer count = cardinality.get(value);
2391             if (count == null) {
2392                 return;
2393             }
2394             try {
2395                 iter.remove();
2396                 cardinality.put(value, --count);
2397             } catch (final UnsupportedOperationException e) {
2398                 // if values.iterator.remove is unsupported, just skip this test
2399                 return;
2400             }
2401             final boolean expected = count > 0;
2402             final StringBuilder msg = new StringBuilder("Value should ");
2403             msg.append(expected ? "yet " : "no longer ");
2404             msg.append("be present in the underlying map");
2405             assertEquals(expected, getMap().containsValue(value), msg.toString());
2406         }
2407         assertTrue(getMap().isEmpty());
2408     }
2409 
2410     /**
2411      * Tests values.removeAll.
2412      */
2413     @Test
2414     public void testValuesRemoveAll() {
2415         resetFull();
2416         final Collection<V> values = getMap().values();
2417         final List<V> sampleValuesAsList = Arrays.asList(getSampleValues());
2418         if (!values.equals(sampleValuesAsList)) {
2419             return;
2420         }
2421         try {
2422             assertFalse(values.removeAll(Collections.<V>emptySet()));
2423         } catch (final UnsupportedOperationException e) {
2424             // if values.removeAll is unsupported, just skip this test
2425             return;
2426         }
2427         assertEquals(sampleValuesAsList.size(), getMap().size());
2428         try {
2429             assertTrue(values.removeAll(sampleValuesAsList));
2430         } catch (final UnsupportedOperationException e) {
2431             // if values.removeAll is unsupported, just skip this test
2432             return;
2433         }
2434         assertTrue(getMap().isEmpty());
2435     }
2436 
2437     /**
2438      * Tests that the {@link Map#bitMaps} collection is backed by the underlying map by removing from the values collection and testing if the value was removed
2439      * from the map.
2440      * <p>
2441      * We should really test the "vice versa" case--that values removed from the map are removed from the values collection--also, but that's a more difficult
2442      * test to construct (lacking a "removeValue" method.)
2443      * </p>
2444      * <p>
2445      * See bug <a href="https://issues.apache.org/jira/browse/COLLECTIONS-92"> COLLECTIONS-92</a>.
2446      * </p>
2447      */
2448     @Test
2449     public void testValuesRemoveChangesMap() {
2450         resetFull();
2451         final V[] sampleValues = getSampleValues();
2452         final Collection<V> values = getMap().values();
2453         for (final V sampleValue : sampleValues) {
2454             if (map.containsValue(sampleValue)) {
2455                 int j = 0; // loop counter prevents infinite loops when remove is broken
2456                 while (values.contains(sampleValue) && j < 10000) {
2457                     try {
2458                         values.remove(sampleValue);
2459                     } catch (final UnsupportedOperationException e) {
2460                         // if values.remove is unsupported, just skip this test
2461                         return;
2462                     }
2463                     j++;
2464                 }
2465                 assertTrue(j < 10000, "values().remove(obj) is broken");
2466                 assertFalse(getMap().containsValue(sampleValue), "Value should have been removed from the underlying map.");
2467             }
2468         }
2469     }
2470 
2471     /**
2472      * Test values.retainAll.
2473      */
2474     @Test
2475     public void testValuesRetainAll() {
2476         resetFull();
2477         final Collection<V> values = getMap().values();
2478         final List<V> sampleValuesAsList = Arrays.asList(getSampleValues());
2479         if (!values.equals(sampleValuesAsList)) {
2480             return;
2481         }
2482         try {
2483             assertFalse(values.retainAll(sampleValuesAsList));
2484         } catch (final UnsupportedOperationException e) {
2485             // if values.retainAll is unsupported, just skip this test
2486             return;
2487         }
2488         assertEquals(sampleValuesAsList.size(), getMap().size());
2489         try {
2490             assertTrue(values.retainAll(Collections.<V>emptySet()));
2491         } catch (final UnsupportedOperationException e) {
2492             // if values.retainAll is unsupported, just skip this test
2493             return;
2494         }
2495         assertTrue(getMap().isEmpty());
2496     }
2497 
2498     /**
2499      * Verifies that {@link #map} is still equal to {@link #confirmed}. This method checks that the map is equal to the HashMap, <em>and</em> that the map's
2500      * collection views are still equal to the HashMap's collection views. An <Code>equals</Code> test is done on the maps and their collection views; their
2501      * size and <Code>isEmpty</Code> results are compared; their hashCodes are compared; and <Code>containsAll</Code> tests are run on the collection views.
2502      */
2503     public void verify() {
2504         verifyMap();
2505         verifyEntrySet();
2506         verifyKeySet();
2507         verifyValues();
2508     }
2509 
2510     public void verifyEntrySet() {
2511         final int size = getConfirmed().size();
2512         final boolean empty = getConfirmed().isEmpty();
2513         assertEquals(size, entrySet.size(), "entrySet should be same size as HashMap's" + "\nTest: " + entrySet + "\nReal: " + getConfirmed().entrySet());
2514         assertEquals(empty, entrySet.isEmpty(), "entrySet should be empty if HashMap is" + "\nTest: " + entrySet + "\nReal: " + getConfirmed().entrySet());
2515         assertTrue(entrySet.containsAll(getConfirmed().entrySet()),
2516                 "entrySet should contain all HashMap's elements" + "\nTest: " + entrySet + "\nReal: " + getConfirmed().entrySet());
2517         assertEquals(getConfirmed().entrySet().hashCode(), entrySet.hashCode(),
2518                 "entrySet hashCodes should be the same" + "\nTest: " + entrySet + "\nReal: " + getConfirmed().entrySet());
2519         assertEquals(getConfirmed().entrySet(), entrySet, "Map's entry set should still equal HashMap's");
2520     }
2521 
2522     public void verifyKeySet() {
2523         final int size = getConfirmed().size();
2524         final boolean empty = getConfirmed().isEmpty();
2525         assertEquals(size, keySet.size(), "keySet should be same size as HashMap's" + "\nTest: " + keySet + "\nReal: " + getConfirmed().keySet());
2526         assertEquals(empty, keySet.isEmpty(), "keySet should be empty if HashMap is" + "\nTest: " + keySet + "\nReal: " + getConfirmed().keySet());
2527         assertTrue(keySet.containsAll(getConfirmed().keySet()),
2528                 "keySet should contain all HashMap's elements" + "\nTest: " + keySet + "\nReal: " + getConfirmed().keySet());
2529         assertEquals(getConfirmed().keySet().hashCode(), keySet.hashCode(),
2530                 "keySet hashCodes should be the same" + "\nTest: " + keySet + "\nReal: " + getConfirmed().keySet());
2531         assertEquals(getConfirmed().keySet(), keySet, "Map's key set should still equal HashMap's");
2532     }
2533 
2534     public void verifyMap() {
2535         final int size = getConfirmed().size();
2536         final boolean empty = getConfirmed().isEmpty();
2537         assertEquals(size, getMap().size(), "Map should be same size as HashMap");
2538         assertEquals(empty, getMap().isEmpty(), "Map should be empty if HashMap is");
2539         assertEquals(getConfirmed().hashCode(), getMap().hashCode(), "hashCodes should be the same");
2540         // changing the order of the assertion below fails for LRUMap because confirmed is
2541         // another collection (for example treemap) and confirmed.equals() creates a normal iterator (not
2542         // #mapIterator()), which modifies the parent expected modCount of the map object, causing
2543         // concurrent modification exceptions.
2544         // Because of this we have assertEquals(map, confirmed), and not the other way around.
2545         assertEquals(map, confirmed, "Map should still equal HashMap");
2546         assertEquals(getMap(), getConfirmed(), "Map should still equal HashMap");
2547     }
2548 
2549     public void verifyValues() {
2550         final List<V> known = new ArrayList<>(getConfirmed().values());
2551 
2552         values = getMap().values();
2553 
2554         final List<V> test = new ArrayList<>(values);
2555 
2556         final int size = getConfirmed().size();
2557         final boolean empty = getConfirmed().isEmpty();
2558         assertEquals(size, values.size(), "values should be same size as HashMap's" + "\nTest: " + test + "\nReal: " + known);
2559         assertEquals(empty, values.isEmpty(), "values should be empty if HashMap is" + "\nTest: " + test + "\nReal: " + known);
2560         assertTrue(test.containsAll(known), "values should contain all HashMap's elements" + "\nTest: " + test + "\nReal: " + known);
2561         assertTrue(known.containsAll(test), "values should contain all HashMap's elements" + "\nTest: " + test + "\nReal: " + known);
2562         // originally coded to use a HashBag, but now separate jar so...
2563         for (final V v : known) {
2564             final boolean removed = test.remove(v);
2565             assertTrue(removed, "Map's values should still equal HashMap's");
2566         }
2567         assertTrue(test.isEmpty(), "Map's values should still equal HashMap's");
2568     }
2569 
2570     /**
2571      * Resets the collection view fields.
2572      */
2573     private void views() {
2574         keySet = getMap().keySet();
2575         // see verifyValues: retrieve the values collection only when verifying them
2576         // this.values = getMap().values();
2577         entrySet = getMap().entrySet();
2578     }
2579 
2580 }