1 /*
2  * Copyright (C) 2008 The Guava Authors
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.google.common.collect.testing;
18 
19 import static java.util.Collections.singleton;
20 
21 import com.google.common.annotations.GwtCompatible;
22 import java.util.Arrays;
23 import java.util.Collection;
24 import java.util.Collections;
25 import java.util.HashSet;
26 import java.util.Iterator;
27 import java.util.Map;
28 import java.util.Map.Entry;
29 import java.util.Set;
30 import junit.framework.TestCase;
31 
32 /**
33  * Tests representing the contract of {@link Map}. Concrete subclasses of this base class test
34  * conformance of concrete {@link Map} subclasses to that contract.
35  *
36  * @param <K> the type of keys used by the maps under test
37  * @param <V> the type of mapped values used the maps under test
38  * @author George van den Driessche
39  */
40 // TODO: Descriptive assertion messages, with hints as to probable fixes.
41 // TODO: Add another constructor parameter indicating whether the class under test is ordered, and
42 // check the order if so.
43 // TODO: Refactor to share code with SetTestBuilder etc.
44 @GwtCompatible
45 public abstract class MapInterfaceTest<K, V> extends TestCase {
46 
47   /** A key type that is not assignable to any classes but Object. */
48   private static final class IncompatibleKeyType {
49     @Override
toString()50     public String toString() {
51       return "IncompatibleKeyType";
52     }
53   }
54 
55   protected final boolean supportsPut;
56   protected final boolean supportsRemove;
57   protected final boolean supportsClear;
58   protected final boolean allowsNullKeys;
59   protected final boolean allowsNullValues;
60   protected final boolean supportsIteratorRemove;
61 
62   /**
63    * Creates a new, empty instance of the class under test.
64    *
65    * @return a new, empty map instance.
66    * @throws UnsupportedOperationException if it's not possible to make an empty instance of the
67    *     class under test.
68    */
makeEmptyMap()69   protected abstract Map<K, V> makeEmptyMap() throws UnsupportedOperationException;
70 
71   /**
72    * Creates a new, non-empty instance of the class under test.
73    *
74    * @return a new, non-empty map instance.
75    * @throws UnsupportedOperationException if it's not possible to make a non-empty instance of the
76    *     class under test.
77    */
makePopulatedMap()78   protected abstract Map<K, V> makePopulatedMap() throws UnsupportedOperationException;
79 
80   /**
81    * Creates a new key that is not expected to be found in {@link #makePopulatedMap()}.
82    *
83    * @return a key.
84    * @throws UnsupportedOperationException if it's not possible to make a key that will not be found
85    *     in the map.
86    */
getKeyNotInPopulatedMap()87   protected abstract K getKeyNotInPopulatedMap() throws UnsupportedOperationException;
88 
89   /**
90    * Creates a new value that is not expected to be found in {@link #makePopulatedMap()}.
91    *
92    * @return a value.
93    * @throws UnsupportedOperationException if it's not possible to make a value that will not be
94    *     found in the map.
95    */
getValueNotInPopulatedMap()96   protected abstract V getValueNotInPopulatedMap() throws UnsupportedOperationException;
97 
98   /**
99    * Constructor that assigns {@code supportsIteratorRemove} the same value as {@code
100    * supportsRemove}.
101    */
MapInterfaceTest( boolean allowsNullKeys, boolean allowsNullValues, boolean supportsPut, boolean supportsRemove, boolean supportsClear)102   protected MapInterfaceTest(
103       boolean allowsNullKeys,
104       boolean allowsNullValues,
105       boolean supportsPut,
106       boolean supportsRemove,
107       boolean supportsClear) {
108     this(
109         allowsNullKeys,
110         allowsNullValues,
111         supportsPut,
112         supportsRemove,
113         supportsClear,
114         supportsRemove);
115   }
116 
117   /** Constructor with an explicit {@code supportsIteratorRemove} parameter. */
MapInterfaceTest( boolean allowsNullKeys, boolean allowsNullValues, boolean supportsPut, boolean supportsRemove, boolean supportsClear, boolean supportsIteratorRemove)118   protected MapInterfaceTest(
119       boolean allowsNullKeys,
120       boolean allowsNullValues,
121       boolean supportsPut,
122       boolean supportsRemove,
123       boolean supportsClear,
124       boolean supportsIteratorRemove) {
125     this.supportsPut = supportsPut;
126     this.supportsRemove = supportsRemove;
127     this.supportsClear = supportsClear;
128     this.allowsNullKeys = allowsNullKeys;
129     this.allowsNullValues = allowsNullValues;
130     this.supportsIteratorRemove = supportsIteratorRemove;
131   }
132 
133   /**
134    * Used by tests that require a map, but don't care whether it's populated or not.
135    *
136    * @return a new map instance.
137    */
makeEitherMap()138   protected Map<K, V> makeEitherMap() {
139     try {
140       return makePopulatedMap();
141     } catch (UnsupportedOperationException e) {
142       return makeEmptyMap();
143     }
144   }
145 
supportsValuesHashCode(Map<K, V> map)146   protected final boolean supportsValuesHashCode(Map<K, V> map) {
147     // get the first non-null value
148     Collection<V> values = map.values();
149     for (V value : values) {
150       if (value != null) {
151         try {
152           value.hashCode();
153         } catch (Exception e) {
154           return false;
155         }
156         return true;
157       }
158     }
159     return true;
160   }
161 
162   /**
163    * Checks all the properties that should always hold of a map. Also calls {@link
164    * #assertMoreInvariants} to check invariants that are peculiar to specific implementations.
165    *
166    * @see #assertMoreInvariants
167    * @param map the map to check.
168    */
assertInvariants(Map<K, V> map)169   protected final void assertInvariants(Map<K, V> map) {
170     Set<K> keySet = map.keySet();
171     Collection<V> valueCollection = map.values();
172     Set<Entry<K, V>> entrySet = map.entrySet();
173 
174     assertEquals(map.size() == 0, map.isEmpty());
175     assertEquals(map.size(), keySet.size());
176     assertEquals(keySet.size() == 0, keySet.isEmpty());
177     assertEquals(!keySet.isEmpty(), keySet.iterator().hasNext());
178 
179     int expectedKeySetHash = 0;
180     for (K key : keySet) {
181       V value = map.get(key);
182       expectedKeySetHash += key != null ? key.hashCode() : 0;
183       assertTrue(map.containsKey(key));
184       assertTrue(map.containsValue(value));
185       assertTrue(valueCollection.contains(value));
186       assertTrue(valueCollection.containsAll(Collections.singleton(value)));
187       assertTrue(entrySet.contains(mapEntry(key, value)));
188       assertTrue(allowsNullKeys || (key != null));
189     }
190     assertEquals(expectedKeySetHash, keySet.hashCode());
191 
192     assertEquals(map.size(), valueCollection.size());
193     assertEquals(valueCollection.size() == 0, valueCollection.isEmpty());
194     assertEquals(!valueCollection.isEmpty(), valueCollection.iterator().hasNext());
195     for (V value : valueCollection) {
196       assertTrue(map.containsValue(value));
197       assertTrue(allowsNullValues || (value != null));
198     }
199 
200     assertEquals(map.size(), entrySet.size());
201     assertEquals(entrySet.size() == 0, entrySet.isEmpty());
202     assertEquals(!entrySet.isEmpty(), entrySet.iterator().hasNext());
203     assertEntrySetNotContainsString(entrySet);
204 
205     boolean supportsValuesHashCode = supportsValuesHashCode(map);
206     if (supportsValuesHashCode) {
207       int expectedEntrySetHash = 0;
208       for (Entry<K, V> entry : entrySet) {
209         assertTrue(map.containsKey(entry.getKey()));
210         assertTrue(map.containsValue(entry.getValue()));
211         int expectedHash =
212             (entry.getKey() == null ? 0 : entry.getKey().hashCode())
213                 ^ (entry.getValue() == null ? 0 : entry.getValue().hashCode());
214         assertEquals(expectedHash, entry.hashCode());
215         expectedEntrySetHash += expectedHash;
216       }
217       assertEquals(expectedEntrySetHash, entrySet.hashCode());
218       assertTrue(entrySet.containsAll(new HashSet<Entry<K, V>>(entrySet)));
219       assertTrue(entrySet.equals(new HashSet<Entry<K, V>>(entrySet)));
220     }
221 
222     Object[] entrySetToArray1 = entrySet.toArray();
223     assertEquals(map.size(), entrySetToArray1.length);
224     assertTrue(Arrays.asList(entrySetToArray1).containsAll(entrySet));
225 
226     Entry<?, ?>[] entrySetToArray2 = new Entry<?, ?>[map.size() + 2];
227     entrySetToArray2[map.size()] = mapEntry("foo", 1);
228     assertSame(entrySetToArray2, entrySet.toArray(entrySetToArray2));
229     assertNull(entrySetToArray2[map.size()]);
230     assertTrue(Arrays.asList(entrySetToArray2).containsAll(entrySet));
231 
232     Object[] valuesToArray1 = valueCollection.toArray();
233     assertEquals(map.size(), valuesToArray1.length);
234     assertTrue(Arrays.asList(valuesToArray1).containsAll(valueCollection));
235 
236     Object[] valuesToArray2 = new Object[map.size() + 2];
237     valuesToArray2[map.size()] = "foo";
238     assertSame(valuesToArray2, valueCollection.toArray(valuesToArray2));
239     assertNull(valuesToArray2[map.size()]);
240     assertTrue(Arrays.asList(valuesToArray2).containsAll(valueCollection));
241 
242     if (supportsValuesHashCode) {
243       int expectedHash = 0;
244       for (Entry<K, V> entry : entrySet) {
245         expectedHash += entry.hashCode();
246       }
247       assertEquals(expectedHash, map.hashCode());
248     }
249 
250     assertMoreInvariants(map);
251   }
252 
253   @SuppressWarnings("CollectionIncompatibleType")
assertEntrySetNotContainsString(Set<Entry<K, V>> entrySet)254   private void assertEntrySetNotContainsString(Set<Entry<K, V>> entrySet) {
255     // Very unlikely that a buggy collection would ever return true. It might accidentally throw.
256     assertFalse(entrySet.contains("foo"));
257   }
258 
259   /**
260    * Override this to check invariants which should hold true for a particular implementation, but
261    * which are not generally applicable to every instance of Map.
262    *
263    * @param map the map whose additional invariants to check.
264    */
assertMoreInvariants(Map<K, V> map)265   protected void assertMoreInvariants(Map<K, V> map) {}
266 
testClear()267   public void testClear() {
268     final Map<K, V> map;
269     try {
270       map = makePopulatedMap();
271     } catch (UnsupportedOperationException e) {
272       return;
273     }
274 
275     if (supportsClear) {
276       map.clear();
277       assertTrue(map.isEmpty());
278     } else {
279       try {
280         map.clear();
281         fail("Expected UnsupportedOperationException.");
282       } catch (UnsupportedOperationException expected) {
283       }
284     }
285     assertInvariants(map);
286   }
287 
testContainsKey()288   public void testContainsKey() {
289     final Map<K, V> map;
290     final K unmappedKey;
291     try {
292       map = makePopulatedMap();
293       unmappedKey = getKeyNotInPopulatedMap();
294     } catch (UnsupportedOperationException e) {
295       return;
296     }
297     assertFalse(map.containsKey(unmappedKey));
298     try {
299       assertFalse(map.containsKey(new IncompatibleKeyType()));
300     } catch (ClassCastException tolerated) {
301     }
302     assertTrue(map.containsKey(map.keySet().iterator().next()));
303     if (allowsNullKeys) {
304       boolean unused = map.containsKey(null);
305     } else {
306       try {
307         boolean unused2 = map.containsKey(null);
308       } catch (NullPointerException optional) {
309       }
310     }
311     assertInvariants(map);
312   }
313 
testContainsValue()314   public void testContainsValue() {
315     final Map<K, V> map;
316     final V unmappedValue;
317     try {
318       map = makePopulatedMap();
319       unmappedValue = getValueNotInPopulatedMap();
320     } catch (UnsupportedOperationException e) {
321       return;
322     }
323     assertFalse(map.containsValue(unmappedValue));
324     assertTrue(map.containsValue(map.values().iterator().next()));
325     if (allowsNullValues) {
326       boolean unused = map.containsValue(null);
327     } else {
328       try {
329         boolean unused2 = map.containsKey(null);
330       } catch (NullPointerException optional) {
331       }
332     }
333     assertInvariants(map);
334   }
335 
testEntrySet()336   public void testEntrySet() {
337     final Map<K, V> map;
338     final Set<Entry<K, V>> entrySet;
339     try {
340       map = makePopulatedMap();
341     } catch (UnsupportedOperationException e) {
342       return;
343     }
344     assertInvariants(map);
345 
346     entrySet = map.entrySet();
347     final K unmappedKey;
348     final V unmappedValue;
349     try {
350       unmappedKey = getKeyNotInPopulatedMap();
351       unmappedValue = getValueNotInPopulatedMap();
352     } catch (UnsupportedOperationException e) {
353       return;
354     }
355     for (Entry<K, V> entry : entrySet) {
356       assertFalse(unmappedKey.equals(entry.getKey()));
357       assertFalse(unmappedValue.equals(entry.getValue()));
358     }
359   }
360 
testEntrySetForEmptyMap()361   public void testEntrySetForEmptyMap() {
362     final Map<K, V> map;
363     try {
364       map = makeEmptyMap();
365     } catch (UnsupportedOperationException e) {
366       return;
367     }
368     assertInvariants(map);
369   }
370 
testEntrySetContainsEntryIncompatibleKey()371   public void testEntrySetContainsEntryIncompatibleKey() {
372     final Map<K, V> map;
373     final Set<Entry<K, V>> entrySet;
374     try {
375       map = makeEitherMap();
376     } catch (UnsupportedOperationException e) {
377       return;
378     }
379     assertInvariants(map);
380 
381     entrySet = map.entrySet();
382     final V unmappedValue;
383     try {
384       unmappedValue = getValueNotInPopulatedMap();
385     } catch (UnsupportedOperationException e) {
386       return;
387     }
388     Entry<IncompatibleKeyType, V> entry = mapEntry(new IncompatibleKeyType(), unmappedValue);
389     try {
390       assertFalse(entrySet.contains(entry));
391     } catch (ClassCastException tolerated) {
392     }
393   }
394 
testEntrySetContainsEntryNullKeyPresent()395   public void testEntrySetContainsEntryNullKeyPresent() {
396     if (!allowsNullKeys || !supportsPut) {
397       return;
398     }
399     final Map<K, V> map;
400     final Set<Entry<K, V>> entrySet;
401     try {
402       map = makeEitherMap();
403     } catch (UnsupportedOperationException e) {
404       return;
405     }
406     assertInvariants(map);
407 
408     entrySet = map.entrySet();
409     final V unmappedValue;
410     try {
411       unmappedValue = getValueNotInPopulatedMap();
412     } catch (UnsupportedOperationException e) {
413       return;
414     }
415 
416     map.put(null, unmappedValue);
417     Entry<K, V> entry = mapEntry(null, unmappedValue);
418     assertTrue(entrySet.contains(entry));
419     assertFalse(entrySet.contains(mapEntry(null, null)));
420   }
421 
testEntrySetContainsEntryNullKeyMissing()422   public void testEntrySetContainsEntryNullKeyMissing() {
423     final Map<K, V> map;
424     final Set<Entry<K, V>> entrySet;
425     try {
426       map = makeEitherMap();
427     } catch (UnsupportedOperationException e) {
428       return;
429     }
430     assertInvariants(map);
431 
432     entrySet = map.entrySet();
433     final V unmappedValue;
434     try {
435       unmappedValue = getValueNotInPopulatedMap();
436     } catch (UnsupportedOperationException e) {
437       return;
438     }
439     Entry<K, V> entry = mapEntry(null, unmappedValue);
440     try {
441       assertFalse(entrySet.contains(entry));
442     } catch (NullPointerException e) {
443       assertFalse(allowsNullKeys);
444     }
445     try {
446       assertFalse(entrySet.contains(mapEntry(null, null)));
447     } catch (NullPointerException e) {
448       assertFalse(allowsNullKeys && allowsNullValues);
449     }
450   }
451 
testEntrySetIteratorRemove()452   public void testEntrySetIteratorRemove() {
453     final Map<K, V> map;
454     try {
455       map = makePopulatedMap();
456     } catch (UnsupportedOperationException e) {
457       return;
458     }
459 
460     Set<Entry<K, V>> entrySet = map.entrySet();
461     Iterator<Entry<K, V>> iterator = entrySet.iterator();
462     if (supportsIteratorRemove) {
463       int initialSize = map.size();
464       Entry<K, V> entry = iterator.next();
465       Entry<K, V> entryCopy = Helpers.mapEntry(entry.getKey(), entry.getValue());
466 
467       iterator.remove();
468       assertEquals(initialSize - 1, map.size());
469 
470       // Use "entryCopy" instead of "entry" because "entry" might be invalidated after
471       // iterator.remove().
472       assertFalse(entrySet.contains(entryCopy));
473       assertInvariants(map);
474       try {
475         iterator.remove();
476         fail("Expected IllegalStateException.");
477       } catch (IllegalStateException expected) {
478       }
479     } else {
480       try {
481         iterator.next();
482         iterator.remove();
483         fail("Expected UnsupportedOperationException.");
484       } catch (UnsupportedOperationException expected) {
485       }
486     }
487     assertInvariants(map);
488   }
489 
testEntrySetRemove()490   public void testEntrySetRemove() {
491     final Map<K, V> map;
492     try {
493       map = makePopulatedMap();
494     } catch (UnsupportedOperationException e) {
495       return;
496     }
497 
498     Set<Entry<K, V>> entrySet = map.entrySet();
499     if (supportsRemove) {
500       int initialSize = map.size();
501       boolean didRemove = entrySet.remove(entrySet.iterator().next());
502       assertTrue(didRemove);
503       assertEquals(initialSize - 1, map.size());
504     } else {
505       try {
506         entrySet.remove(entrySet.iterator().next());
507         fail("Expected UnsupportedOperationException.");
508       } catch (UnsupportedOperationException expected) {
509       }
510     }
511     assertInvariants(map);
512   }
513 
testEntrySetRemoveMissingKey()514   public void testEntrySetRemoveMissingKey() {
515     final Map<K, V> map;
516     final K key;
517     try {
518       map = makeEitherMap();
519       key = getKeyNotInPopulatedMap();
520     } catch (UnsupportedOperationException e) {
521       return;
522     }
523 
524     Set<Entry<K, V>> entrySet = map.entrySet();
525     Entry<K, V> entry = mapEntry(key, getValueNotInPopulatedMap());
526     int initialSize = map.size();
527     if (supportsRemove) {
528       boolean didRemove = entrySet.remove(entry);
529       assertFalse(didRemove);
530     } else {
531       try {
532         boolean didRemove = entrySet.remove(entry);
533         assertFalse(didRemove);
534       } catch (UnsupportedOperationException optional) {
535       }
536     }
537     assertEquals(initialSize, map.size());
538     assertFalse(map.containsKey(key));
539     assertInvariants(map);
540   }
541 
testEntrySetRemoveDifferentValue()542   public void testEntrySetRemoveDifferentValue() {
543     final Map<K, V> map;
544     try {
545       map = makePopulatedMap();
546     } catch (UnsupportedOperationException e) {
547       return;
548     }
549 
550     Set<Entry<K, V>> entrySet = map.entrySet();
551     K key = map.keySet().iterator().next();
552     Entry<K, V> entry = mapEntry(key, getValueNotInPopulatedMap());
553     int initialSize = map.size();
554     if (supportsRemove) {
555       boolean didRemove = entrySet.remove(entry);
556       assertFalse(didRemove);
557     } else {
558       try {
559         boolean didRemove = entrySet.remove(entry);
560         assertFalse(didRemove);
561       } catch (UnsupportedOperationException optional) {
562       }
563     }
564     assertEquals(initialSize, map.size());
565     assertTrue(map.containsKey(key));
566     assertInvariants(map);
567   }
568 
testEntrySetRemoveNullKeyPresent()569   public void testEntrySetRemoveNullKeyPresent() {
570     if (!allowsNullKeys || !supportsPut || !supportsRemove) {
571       return;
572     }
573     final Map<K, V> map;
574     final Set<Entry<K, V>> entrySet;
575     try {
576       map = makeEitherMap();
577     } catch (UnsupportedOperationException e) {
578       return;
579     }
580     assertInvariants(map);
581 
582     entrySet = map.entrySet();
583     final V unmappedValue;
584     try {
585       unmappedValue = getValueNotInPopulatedMap();
586     } catch (UnsupportedOperationException e) {
587       return;
588     }
589 
590     map.put(null, unmappedValue);
591     assertEquals(unmappedValue, map.get(null));
592     assertTrue(map.containsKey(null));
593     Entry<K, V> entry = mapEntry(null, unmappedValue);
594     assertTrue(entrySet.remove(entry));
595     assertNull(map.get(null));
596     assertFalse(map.containsKey(null));
597   }
598 
testEntrySetRemoveNullKeyMissing()599   public void testEntrySetRemoveNullKeyMissing() {
600     final Map<K, V> map;
601     try {
602       map = makeEitherMap();
603     } catch (UnsupportedOperationException e) {
604       return;
605     }
606 
607     Set<Entry<K, V>> entrySet = map.entrySet();
608     Entry<K, V> entry = mapEntry(null, getValueNotInPopulatedMap());
609     int initialSize = map.size();
610     if (supportsRemove) {
611       try {
612         boolean didRemove = entrySet.remove(entry);
613         assertFalse(didRemove);
614       } catch (NullPointerException e) {
615         assertFalse(allowsNullKeys);
616       }
617     } else {
618       try {
619         boolean didRemove = entrySet.remove(entry);
620         assertFalse(didRemove);
621       } catch (UnsupportedOperationException optional) {
622       }
623     }
624     assertEquals(initialSize, map.size());
625     assertInvariants(map);
626   }
627 
testEntrySetRemoveAll()628   public void testEntrySetRemoveAll() {
629     final Map<K, V> map;
630     try {
631       map = makePopulatedMap();
632     } catch (UnsupportedOperationException e) {
633       return;
634     }
635 
636     Set<Entry<K, V>> entrySet = map.entrySet();
637 
638     Entry<K, V> entryToRemove = entrySet.iterator().next();
639     Set<Entry<K, V>> entriesToRemove = singleton(entryToRemove);
640     if (supportsRemove) {
641       // We use a copy of "entryToRemove" in the assertion because "entryToRemove" might be
642       // invalidated and have undefined behavior after entrySet.removeAll(entriesToRemove),
643       // for example entryToRemove.getValue() might be null.
644       Entry<K, V> entryToRemoveCopy =
645           Helpers.mapEntry(entryToRemove.getKey(), entryToRemove.getValue());
646 
647       int initialSize = map.size();
648       boolean didRemove = entrySet.removeAll(entriesToRemove);
649       assertTrue(didRemove);
650       assertEquals(initialSize - entriesToRemove.size(), map.size());
651 
652       // Use "entryToRemoveCopy" instead of "entryToRemove" because it might be invalidated and
653       // have undefined behavior after entrySet.removeAll(entriesToRemove),
654       assertFalse(entrySet.contains(entryToRemoveCopy));
655     } else {
656       try {
657         entrySet.removeAll(entriesToRemove);
658         fail("Expected UnsupportedOperationException.");
659       } catch (UnsupportedOperationException expected) {
660       }
661     }
662     assertInvariants(map);
663   }
664 
testEntrySetRemoveAllNullFromEmpty()665   public void testEntrySetRemoveAllNullFromEmpty() {
666     final Map<K, V> map;
667     try {
668       map = makeEmptyMap();
669     } catch (UnsupportedOperationException e) {
670       return;
671     }
672 
673     Set<Entry<K, V>> entrySet = map.entrySet();
674     if (supportsRemove) {
675       try {
676         entrySet.removeAll(null);
677         fail("Expected NullPointerException.");
678       } catch (NullPointerException expected) {
679       }
680     } else {
681       try {
682         entrySet.removeAll(null);
683         fail("Expected UnsupportedOperationException or NullPointerException.");
684       } catch (UnsupportedOperationException | NullPointerException e) {
685         // Expected.
686       }
687     }
688     assertInvariants(map);
689   }
690 
testEntrySetRetainAll()691   public void testEntrySetRetainAll() {
692     final Map<K, V> map;
693     try {
694       map = makePopulatedMap();
695     } catch (UnsupportedOperationException e) {
696       return;
697     }
698 
699     Set<Entry<K, V>> entrySet = map.entrySet();
700     Set<Entry<K, V>> entriesToRetain = singleton(entrySet.iterator().next());
701     if (supportsRemove) {
702       boolean shouldRemove = (entrySet.size() > entriesToRetain.size());
703       boolean didRemove = entrySet.retainAll(entriesToRetain);
704       assertEquals(shouldRemove, didRemove);
705       assertEquals(entriesToRetain.size(), map.size());
706       for (Entry<K, V> entry : entriesToRetain) {
707         assertTrue(entrySet.contains(entry));
708       }
709     } else {
710       try {
711         entrySet.retainAll(entriesToRetain);
712         fail("Expected UnsupportedOperationException.");
713       } catch (UnsupportedOperationException expected) {
714       }
715     }
716     assertInvariants(map);
717   }
718 
testEntrySetRetainAllNullFromEmpty()719   public void testEntrySetRetainAllNullFromEmpty() {
720     final Map<K, V> map;
721     try {
722       map = makeEmptyMap();
723     } catch (UnsupportedOperationException e) {
724       return;
725     }
726 
727     Set<Entry<K, V>> entrySet = map.entrySet();
728     if (supportsRemove) {
729       try {
730         entrySet.retainAll(null);
731         // Returning successfully is not ideal, but tolerated.
732       } catch (NullPointerException expected) {
733       }
734     } else {
735       try {
736         entrySet.retainAll(null);
737         // We have to tolerate a successful return (Sun bug 4802647)
738       } catch (UnsupportedOperationException | NullPointerException e) {
739         // Expected.
740       }
741     }
742     assertInvariants(map);
743   }
744 
testEntrySetClear()745   public void testEntrySetClear() {
746     final Map<K, V> map;
747     try {
748       map = makePopulatedMap();
749     } catch (UnsupportedOperationException e) {
750       return;
751     }
752 
753     Set<Entry<K, V>> entrySet = map.entrySet();
754     if (supportsClear) {
755       entrySet.clear();
756       assertTrue(entrySet.isEmpty());
757     } else {
758       try {
759         entrySet.clear();
760         fail("Expected UnsupportedOperationException.");
761       } catch (UnsupportedOperationException expected) {
762       }
763     }
764     assertInvariants(map);
765   }
766 
testEntrySetAddAndAddAll()767   public void testEntrySetAddAndAddAll() {
768     final Map<K, V> map = makeEitherMap();
769 
770     Set<Entry<K, V>> entrySet = map.entrySet();
771     final Entry<K, V> entryToAdd = mapEntry(null, null);
772     try {
773       entrySet.add(entryToAdd);
774       fail("Expected UnsupportedOperationException or NullPointerException.");
775     } catch (UnsupportedOperationException | NullPointerException e) {
776       // Expected.
777     }
778     assertInvariants(map);
779 
780     try {
781       entrySet.addAll(singleton(entryToAdd));
782       fail("Expected UnsupportedOperationException or NullPointerException.");
783     } catch (UnsupportedOperationException | NullPointerException e) {
784       // Expected.
785     }
786     assertInvariants(map);
787   }
788 
testEntrySetSetValue()789   public void testEntrySetSetValue() {
790     // TODO: Investigate the extent to which, in practice, maps that support
791     // put() also support Entry.setValue().
792     if (!supportsPut) {
793       return;
794     }
795 
796     final Map<K, V> map;
797     final V valueToSet;
798     try {
799       map = makePopulatedMap();
800       valueToSet = getValueNotInPopulatedMap();
801     } catch (UnsupportedOperationException e) {
802       return;
803     }
804 
805     Set<Entry<K, V>> entrySet = map.entrySet();
806     Entry<K, V> entry = entrySet.iterator().next();
807     final V oldValue = entry.getValue();
808     final V returnedValue = entry.setValue(valueToSet);
809     assertEquals(oldValue, returnedValue);
810     assertTrue(entrySet.contains(mapEntry(entry.getKey(), valueToSet)));
811     assertEquals(valueToSet, map.get(entry.getKey()));
812     assertInvariants(map);
813   }
814 
testEntrySetSetValueSameValue()815   public void testEntrySetSetValueSameValue() {
816     // TODO: Investigate the extent to which, in practice, maps that support
817     // put() also support Entry.setValue().
818     if (!supportsPut) {
819       return;
820     }
821 
822     final Map<K, V> map;
823     try {
824       map = makePopulatedMap();
825     } catch (UnsupportedOperationException e) {
826       return;
827     }
828 
829     Set<Entry<K, V>> entrySet = map.entrySet();
830     Entry<K, V> entry = entrySet.iterator().next();
831     final V oldValue = entry.getValue();
832     final V returnedValue = entry.setValue(oldValue);
833     assertEquals(oldValue, returnedValue);
834     assertTrue(entrySet.contains(mapEntry(entry.getKey(), oldValue)));
835     assertEquals(oldValue, map.get(entry.getKey()));
836     assertInvariants(map);
837   }
838 
testEqualsForEqualMap()839   public void testEqualsForEqualMap() {
840     final Map<K, V> map;
841     try {
842       map = makePopulatedMap();
843     } catch (UnsupportedOperationException e) {
844       return;
845     }
846 
847     assertEquals(map, map);
848     assertEquals(makePopulatedMap(), map);
849     assertFalse(map.equals(Collections.emptyMap()));
850     // no-inspection ObjectEqualsNull
851     assertFalse(map.equals(null));
852   }
853 
testEqualsForLargerMap()854   public void testEqualsForLargerMap() {
855     if (!supportsPut) {
856       return;
857     }
858 
859     final Map<K, V> map;
860     final Map<K, V> largerMap;
861     try {
862       map = makePopulatedMap();
863       largerMap = makePopulatedMap();
864       largerMap.put(getKeyNotInPopulatedMap(), getValueNotInPopulatedMap());
865     } catch (UnsupportedOperationException e) {
866       return;
867     }
868 
869     assertFalse(map.equals(largerMap));
870   }
871 
testEqualsForSmallerMap()872   public void testEqualsForSmallerMap() {
873     if (!supportsRemove) {
874       return;
875     }
876 
877     final Map<K, V> map;
878     final Map<K, V> smallerMap;
879     try {
880       map = makePopulatedMap();
881       smallerMap = makePopulatedMap();
882       smallerMap.remove(smallerMap.keySet().iterator().next());
883     } catch (UnsupportedOperationException e) {
884       return;
885     }
886 
887     assertFalse(map.equals(smallerMap));
888   }
889 
testEqualsForEmptyMap()890   public void testEqualsForEmptyMap() {
891     final Map<K, V> map;
892     try {
893       map = makeEmptyMap();
894     } catch (UnsupportedOperationException e) {
895       return;
896     }
897 
898     assertEquals(map, map);
899     assertEquals(makeEmptyMap(), map);
900     assertEquals(Collections.emptyMap(), map);
901     assertFalse(map.equals(Collections.emptySet()));
902     // noinspection ObjectEqualsNull
903     assertFalse(map.equals(null));
904   }
905 
testGet()906   public void testGet() {
907     final Map<K, V> map;
908     try {
909       map = makePopulatedMap();
910     } catch (UnsupportedOperationException e) {
911       return;
912     }
913 
914     for (Entry<K, V> entry : map.entrySet()) {
915       assertEquals(entry.getValue(), map.get(entry.getKey()));
916     }
917 
918     K unmappedKey = null;
919     try {
920       unmappedKey = getKeyNotInPopulatedMap();
921     } catch (UnsupportedOperationException e) {
922       return;
923     }
924     assertNull(map.get(unmappedKey));
925   }
926 
testGetForEmptyMap()927   public void testGetForEmptyMap() {
928     final Map<K, V> map;
929     K unmappedKey = null;
930     try {
931       map = makeEmptyMap();
932       unmappedKey = getKeyNotInPopulatedMap();
933     } catch (UnsupportedOperationException e) {
934       return;
935     }
936     assertNull(map.get(unmappedKey));
937   }
938 
testGetNull()939   public void testGetNull() {
940     Map<K, V> map = makeEitherMap();
941     if (allowsNullKeys) {
942       if (allowsNullValues) {
943         // TODO: decide what to test here.
944       } else {
945         assertEquals(map.containsKey(null), map.get(null) != null);
946       }
947     } else {
948       try {
949         map.get(null);
950       } catch (NullPointerException optional) {
951       }
952     }
953     assertInvariants(map);
954   }
955 
testHashCode()956   public void testHashCode() {
957     final Map<K, V> map;
958     try {
959       map = makePopulatedMap();
960     } catch (UnsupportedOperationException e) {
961       return;
962     }
963     assertInvariants(map);
964   }
965 
testHashCodeForEmptyMap()966   public void testHashCodeForEmptyMap() {
967     final Map<K, V> map;
968     try {
969       map = makeEmptyMap();
970     } catch (UnsupportedOperationException e) {
971       return;
972     }
973     assertInvariants(map);
974   }
975 
testPutNewKey()976   public void testPutNewKey() {
977     final Map<K, V> map = makeEitherMap();
978     final K keyToPut;
979     final V valueToPut;
980     try {
981       keyToPut = getKeyNotInPopulatedMap();
982       valueToPut = getValueNotInPopulatedMap();
983     } catch (UnsupportedOperationException e) {
984       return;
985     }
986     if (supportsPut) {
987       int initialSize = map.size();
988       V oldValue = map.put(keyToPut, valueToPut);
989       assertEquals(valueToPut, map.get(keyToPut));
990       assertTrue(map.containsKey(keyToPut));
991       assertTrue(map.containsValue(valueToPut));
992       assertEquals(initialSize + 1, map.size());
993       assertNull(oldValue);
994     } else {
995       try {
996         map.put(keyToPut, valueToPut);
997         fail("Expected UnsupportedOperationException.");
998       } catch (UnsupportedOperationException expected) {
999       }
1000     }
1001     assertInvariants(map);
1002   }
1003 
testPutExistingKey()1004   public void testPutExistingKey() {
1005     final Map<K, V> map;
1006     final K keyToPut;
1007     final V valueToPut;
1008     try {
1009       map = makePopulatedMap();
1010       valueToPut = getValueNotInPopulatedMap();
1011     } catch (UnsupportedOperationException e) {
1012       return;
1013     }
1014     keyToPut = map.keySet().iterator().next();
1015     if (supportsPut) {
1016       int initialSize = map.size();
1017       map.put(keyToPut, valueToPut);
1018       assertEquals(valueToPut, map.get(keyToPut));
1019       assertTrue(map.containsKey(keyToPut));
1020       assertTrue(map.containsValue(valueToPut));
1021       assertEquals(initialSize, map.size());
1022     } else {
1023       try {
1024         map.put(keyToPut, valueToPut);
1025         fail("Expected UnsupportedOperationException.");
1026       } catch (UnsupportedOperationException expected) {
1027       }
1028     }
1029     assertInvariants(map);
1030   }
1031 
testPutNullKey()1032   public void testPutNullKey() {
1033     if (!supportsPut) {
1034       return;
1035     }
1036     final Map<K, V> map = makeEitherMap();
1037     final V valueToPut;
1038     try {
1039       valueToPut = getValueNotInPopulatedMap();
1040     } catch (UnsupportedOperationException e) {
1041       return;
1042     }
1043     if (allowsNullKeys) {
1044       final V oldValue = map.get(null);
1045       final V returnedValue = map.put(null, valueToPut);
1046       assertEquals(oldValue, returnedValue);
1047       assertEquals(valueToPut, map.get(null));
1048       assertTrue(map.containsKey(null));
1049       assertTrue(map.containsValue(valueToPut));
1050     } else {
1051       try {
1052         map.put(null, valueToPut);
1053         fail("Expected RuntimeException");
1054       } catch (RuntimeException expected) {
1055       }
1056     }
1057     assertInvariants(map);
1058   }
1059 
testPutNullValue()1060   public void testPutNullValue() {
1061     if (!supportsPut) {
1062       return;
1063     }
1064     final Map<K, V> map = makeEitherMap();
1065     final K keyToPut;
1066     try {
1067       keyToPut = getKeyNotInPopulatedMap();
1068     } catch (UnsupportedOperationException e) {
1069       return;
1070     }
1071     if (allowsNullValues) {
1072       int initialSize = map.size();
1073       final V oldValue = map.get(keyToPut);
1074       final V returnedValue = map.put(keyToPut, null);
1075       assertEquals(oldValue, returnedValue);
1076       assertNull(map.get(keyToPut));
1077       assertTrue(map.containsKey(keyToPut));
1078       assertTrue(map.containsValue(null));
1079       assertEquals(initialSize + 1, map.size());
1080     } else {
1081       try {
1082         map.put(keyToPut, null);
1083         fail("Expected RuntimeException");
1084       } catch (RuntimeException expected) {
1085       }
1086     }
1087     assertInvariants(map);
1088   }
1089 
testPutNullValueForExistingKey()1090   public void testPutNullValueForExistingKey() {
1091     if (!supportsPut) {
1092       return;
1093     }
1094     final Map<K, V> map;
1095     final K keyToPut;
1096     try {
1097       map = makePopulatedMap();
1098       keyToPut = map.keySet().iterator().next();
1099     } catch (UnsupportedOperationException e) {
1100       return;
1101     }
1102     if (allowsNullValues) {
1103       int initialSize = map.size();
1104       final V oldValue = map.get(keyToPut);
1105       final V returnedValue = map.put(keyToPut, null);
1106       assertEquals(oldValue, returnedValue);
1107       assertNull(map.get(keyToPut));
1108       assertTrue(map.containsKey(keyToPut));
1109       assertTrue(map.containsValue(null));
1110       assertEquals(initialSize, map.size());
1111     } else {
1112       try {
1113         map.put(keyToPut, null);
1114         fail("Expected RuntimeException");
1115       } catch (RuntimeException expected) {
1116       }
1117     }
1118     assertInvariants(map);
1119   }
1120 
testPutAllNewKey()1121   public void testPutAllNewKey() {
1122     final Map<K, V> map = makeEitherMap();
1123     final K keyToPut;
1124     final V valueToPut;
1125     try {
1126       keyToPut = getKeyNotInPopulatedMap();
1127       valueToPut = getValueNotInPopulatedMap();
1128     } catch (UnsupportedOperationException e) {
1129       return;
1130     }
1131     final Map<K, V> mapToPut = Collections.singletonMap(keyToPut, valueToPut);
1132     if (supportsPut) {
1133       int initialSize = map.size();
1134       map.putAll(mapToPut);
1135       assertEquals(valueToPut, map.get(keyToPut));
1136       assertTrue(map.containsKey(keyToPut));
1137       assertTrue(map.containsValue(valueToPut));
1138       assertEquals(initialSize + 1, map.size());
1139     } else {
1140       try {
1141         map.putAll(mapToPut);
1142         fail("Expected UnsupportedOperationException.");
1143       } catch (UnsupportedOperationException expected) {
1144       }
1145     }
1146     assertInvariants(map);
1147   }
1148 
testPutAllExistingKey()1149   public void testPutAllExistingKey() {
1150     final Map<K, V> map;
1151     final K keyToPut;
1152     final V valueToPut;
1153     try {
1154       map = makePopulatedMap();
1155       valueToPut = getValueNotInPopulatedMap();
1156     } catch (UnsupportedOperationException e) {
1157       return;
1158     }
1159     keyToPut = map.keySet().iterator().next();
1160     final Map<K, V> mapToPut = Collections.singletonMap(keyToPut, valueToPut);
1161     int initialSize = map.size();
1162     if (supportsPut) {
1163       map.putAll(mapToPut);
1164       assertEquals(valueToPut, map.get(keyToPut));
1165       assertTrue(map.containsKey(keyToPut));
1166       assertTrue(map.containsValue(valueToPut));
1167     } else {
1168       try {
1169         map.putAll(mapToPut);
1170         fail("Expected UnsupportedOperationException.");
1171       } catch (UnsupportedOperationException expected) {
1172       }
1173     }
1174     assertEquals(initialSize, map.size());
1175     assertInvariants(map);
1176   }
1177 
testRemove()1178   public void testRemove() {
1179     final Map<K, V> map;
1180     final K keyToRemove;
1181     try {
1182       map = makePopulatedMap();
1183     } catch (UnsupportedOperationException e) {
1184       return;
1185     }
1186     keyToRemove = map.keySet().iterator().next();
1187     if (supportsRemove) {
1188       int initialSize = map.size();
1189       V expectedValue = map.get(keyToRemove);
1190       V oldValue = map.remove(keyToRemove);
1191       assertEquals(expectedValue, oldValue);
1192       assertFalse(map.containsKey(keyToRemove));
1193       assertEquals(initialSize - 1, map.size());
1194     } else {
1195       try {
1196         map.remove(keyToRemove);
1197         fail("Expected UnsupportedOperationException.");
1198       } catch (UnsupportedOperationException expected) {
1199       }
1200     }
1201     assertInvariants(map);
1202   }
1203 
testRemoveMissingKey()1204   public void testRemoveMissingKey() {
1205     final Map<K, V> map;
1206     final K keyToRemove;
1207     try {
1208       map = makePopulatedMap();
1209       keyToRemove = getKeyNotInPopulatedMap();
1210     } catch (UnsupportedOperationException e) {
1211       return;
1212     }
1213     if (supportsRemove) {
1214       int initialSize = map.size();
1215       assertNull(map.remove(keyToRemove));
1216       assertEquals(initialSize, map.size());
1217     } else {
1218       try {
1219         map.remove(keyToRemove);
1220         fail("Expected UnsupportedOperationException.");
1221       } catch (UnsupportedOperationException expected) {
1222       }
1223     }
1224     assertInvariants(map);
1225   }
1226 
testSize()1227   public void testSize() {
1228     assertInvariants(makeEitherMap());
1229   }
1230 
testKeySetRemove()1231   public void testKeySetRemove() {
1232     final Map<K, V> map;
1233     try {
1234       map = makePopulatedMap();
1235     } catch (UnsupportedOperationException e) {
1236       return;
1237     }
1238 
1239     Set<K> keys = map.keySet();
1240     K key = keys.iterator().next();
1241     if (supportsRemove) {
1242       int initialSize = map.size();
1243       keys.remove(key);
1244       assertEquals(initialSize - 1, map.size());
1245       assertFalse(map.containsKey(key));
1246     } else {
1247       try {
1248         keys.remove(key);
1249         fail("Expected UnsupportedOperationException.");
1250       } catch (UnsupportedOperationException expected) {
1251       }
1252     }
1253     assertInvariants(map);
1254   }
1255 
testKeySetRemoveAll()1256   public void testKeySetRemoveAll() {
1257     final Map<K, V> map;
1258     try {
1259       map = makePopulatedMap();
1260     } catch (UnsupportedOperationException e) {
1261       return;
1262     }
1263 
1264     Set<K> keys = map.keySet();
1265     K key = keys.iterator().next();
1266     if (supportsRemove) {
1267       int initialSize = map.size();
1268       assertTrue(keys.removeAll(Collections.singleton(key)));
1269       assertEquals(initialSize - 1, map.size());
1270       assertFalse(map.containsKey(key));
1271     } else {
1272       try {
1273         keys.removeAll(Collections.singleton(key));
1274         fail("Expected UnsupportedOperationException.");
1275       } catch (UnsupportedOperationException expected) {
1276       }
1277     }
1278     assertInvariants(map);
1279   }
1280 
testKeySetRetainAll()1281   public void testKeySetRetainAll() {
1282     final Map<K, V> map;
1283     try {
1284       map = makePopulatedMap();
1285     } catch (UnsupportedOperationException e) {
1286       return;
1287     }
1288 
1289     Set<K> keys = map.keySet();
1290     K key = keys.iterator().next();
1291     if (supportsRemove) {
1292       keys.retainAll(Collections.singleton(key));
1293       assertEquals(1, map.size());
1294       assertTrue(map.containsKey(key));
1295     } else {
1296       try {
1297         keys.retainAll(Collections.singleton(key));
1298         fail("Expected UnsupportedOperationException.");
1299       } catch (UnsupportedOperationException expected) {
1300       }
1301     }
1302     assertInvariants(map);
1303   }
1304 
testKeySetClear()1305   public void testKeySetClear() {
1306     final Map<K, V> map;
1307     try {
1308       map = makeEitherMap();
1309     } catch (UnsupportedOperationException e) {
1310       return;
1311     }
1312 
1313     Set<K> keySet = map.keySet();
1314     if (supportsClear) {
1315       keySet.clear();
1316       assertTrue(keySet.isEmpty());
1317     } else {
1318       try {
1319         keySet.clear();
1320         fail("Expected UnsupportedOperationException.");
1321       } catch (UnsupportedOperationException expected) {
1322       }
1323     }
1324     assertInvariants(map);
1325   }
1326 
testKeySetRemoveAllNullFromEmpty()1327   public void testKeySetRemoveAllNullFromEmpty() {
1328     final Map<K, V> map;
1329     try {
1330       map = makeEmptyMap();
1331     } catch (UnsupportedOperationException e) {
1332       return;
1333     }
1334 
1335     Set<K> keySet = map.keySet();
1336     if (supportsRemove) {
1337       try {
1338         keySet.removeAll(null);
1339         fail("Expected NullPointerException.");
1340       } catch (NullPointerException expected) {
1341       }
1342     } else {
1343       try {
1344         keySet.removeAll(null);
1345         fail("Expected UnsupportedOperationException or NullPointerException.");
1346       } catch (UnsupportedOperationException | NullPointerException e) {
1347         // Expected.
1348       }
1349     }
1350     assertInvariants(map);
1351   }
1352 
testKeySetRetainAllNullFromEmpty()1353   public void testKeySetRetainAllNullFromEmpty() {
1354     final Map<K, V> map;
1355     try {
1356       map = makeEmptyMap();
1357     } catch (UnsupportedOperationException e) {
1358       return;
1359     }
1360 
1361     Set<K> keySet = map.keySet();
1362     if (supportsRemove) {
1363       try {
1364         keySet.retainAll(null);
1365         // Returning successfully is not ideal, but tolerated.
1366       } catch (NullPointerException expected) {
1367       }
1368     } else {
1369       try {
1370         keySet.retainAll(null);
1371         // We have to tolerate a successful return (Sun bug 4802647)
1372       } catch (UnsupportedOperationException | NullPointerException e) {
1373         // Expected.
1374       }
1375     }
1376     assertInvariants(map);
1377   }
1378 
testValues()1379   public void testValues() {
1380     final Map<K, V> map;
1381     final Collection<V> valueCollection;
1382     try {
1383       map = makePopulatedMap();
1384     } catch (UnsupportedOperationException e) {
1385       return;
1386     }
1387     assertInvariants(map);
1388 
1389     valueCollection = map.values();
1390     final V unmappedValue;
1391     try {
1392       unmappedValue = getValueNotInPopulatedMap();
1393     } catch (UnsupportedOperationException e) {
1394       return;
1395     }
1396     for (V value : valueCollection) {
1397       assertFalse(unmappedValue.equals(value));
1398     }
1399   }
1400 
testValuesIteratorRemove()1401   public void testValuesIteratorRemove() {
1402     final Map<K, V> map;
1403     try {
1404       map = makePopulatedMap();
1405     } catch (UnsupportedOperationException e) {
1406       return;
1407     }
1408 
1409     Collection<V> valueCollection = map.values();
1410     Iterator<V> iterator = valueCollection.iterator();
1411     if (supportsIteratorRemove) {
1412       int initialSize = map.size();
1413       iterator.next();
1414       iterator.remove();
1415       assertEquals(initialSize - 1, map.size());
1416       // (We can't assert that the values collection no longer contains the
1417       // removed value, because the underlying map can have multiple mappings
1418       // to the same value.)
1419       assertInvariants(map);
1420       try {
1421         iterator.remove();
1422         fail("Expected IllegalStateException.");
1423       } catch (IllegalStateException expected) {
1424       }
1425     } else {
1426       try {
1427         iterator.next();
1428         iterator.remove();
1429         fail("Expected UnsupportedOperationException.");
1430       } catch (UnsupportedOperationException expected) {
1431       }
1432     }
1433     assertInvariants(map);
1434   }
1435 
testValuesRemove()1436   public void testValuesRemove() {
1437     final Map<K, V> map;
1438     try {
1439       map = makePopulatedMap();
1440     } catch (UnsupportedOperationException e) {
1441       return;
1442     }
1443 
1444     Collection<V> valueCollection = map.values();
1445     if (supportsRemove) {
1446       int initialSize = map.size();
1447       valueCollection.remove(valueCollection.iterator().next());
1448       assertEquals(initialSize - 1, map.size());
1449       // (We can't assert that the values collection no longer contains the
1450       // removed value, because the underlying map can have multiple mappings
1451       // to the same value.)
1452     } else {
1453       try {
1454         valueCollection.remove(valueCollection.iterator().next());
1455         fail("Expected UnsupportedOperationException.");
1456       } catch (UnsupportedOperationException expected) {
1457       }
1458     }
1459     assertInvariants(map);
1460   }
1461 
testValuesRemoveMissing()1462   public void testValuesRemoveMissing() {
1463     final Map<K, V> map;
1464     final V valueToRemove;
1465     try {
1466       map = makeEitherMap();
1467       valueToRemove = getValueNotInPopulatedMap();
1468     } catch (UnsupportedOperationException e) {
1469       return;
1470     }
1471 
1472     Collection<V> valueCollection = map.values();
1473     int initialSize = map.size();
1474     if (supportsRemove) {
1475       assertFalse(valueCollection.remove(valueToRemove));
1476     } else {
1477       try {
1478         assertFalse(valueCollection.remove(valueToRemove));
1479       } catch (UnsupportedOperationException e) {
1480         // Tolerated.
1481       }
1482     }
1483     assertEquals(initialSize, map.size());
1484     assertInvariants(map);
1485   }
1486 
testValuesRemoveAll()1487   public void testValuesRemoveAll() {
1488     final Map<K, V> map;
1489     try {
1490       map = makePopulatedMap();
1491     } catch (UnsupportedOperationException e) {
1492       return;
1493     }
1494 
1495     Collection<V> valueCollection = map.values();
1496     Set<V> valuesToRemove = singleton(valueCollection.iterator().next());
1497     if (supportsRemove) {
1498       valueCollection.removeAll(valuesToRemove);
1499       for (V value : valuesToRemove) {
1500         assertFalse(valueCollection.contains(value));
1501       }
1502       for (V value : valueCollection) {
1503         assertFalse(valuesToRemove.contains(value));
1504       }
1505     } else {
1506       try {
1507         valueCollection.removeAll(valuesToRemove);
1508         fail("Expected UnsupportedOperationException.");
1509       } catch (UnsupportedOperationException expected) {
1510       }
1511     }
1512     assertInvariants(map);
1513   }
1514 
testValuesRemoveAllNullFromEmpty()1515   public void testValuesRemoveAllNullFromEmpty() {
1516     final Map<K, V> map;
1517     try {
1518       map = makeEmptyMap();
1519     } catch (UnsupportedOperationException e) {
1520       return;
1521     }
1522 
1523     Collection<V> values = map.values();
1524     if (supportsRemove) {
1525       try {
1526         values.removeAll(null);
1527         // Returning successfully is not ideal, but tolerated.
1528       } catch (NullPointerException expected) {
1529       }
1530     } else {
1531       try {
1532         values.removeAll(null);
1533         // We have to tolerate a successful return (Sun bug 4802647)
1534       } catch (UnsupportedOperationException | NullPointerException e) {
1535         // Expected.
1536       }
1537     }
1538     assertInvariants(map);
1539   }
1540 
testValuesRetainAll()1541   public void testValuesRetainAll() {
1542     final Map<K, V> map;
1543     try {
1544       map = makePopulatedMap();
1545     } catch (UnsupportedOperationException e) {
1546       return;
1547     }
1548 
1549     Collection<V> valueCollection = map.values();
1550     Set<V> valuesToRetain = singleton(valueCollection.iterator().next());
1551     if (supportsRemove) {
1552       valueCollection.retainAll(valuesToRetain);
1553       for (V value : valuesToRetain) {
1554         assertTrue(valueCollection.contains(value));
1555       }
1556       for (V value : valueCollection) {
1557         assertTrue(valuesToRetain.contains(value));
1558       }
1559     } else {
1560       try {
1561         valueCollection.retainAll(valuesToRetain);
1562         fail("Expected UnsupportedOperationException.");
1563       } catch (UnsupportedOperationException expected) {
1564       }
1565     }
1566     assertInvariants(map);
1567   }
1568 
testValuesRetainAllNullFromEmpty()1569   public void testValuesRetainAllNullFromEmpty() {
1570     final Map<K, V> map;
1571     try {
1572       map = makeEmptyMap();
1573     } catch (UnsupportedOperationException e) {
1574       return;
1575     }
1576 
1577     Collection<V> values = map.values();
1578     if (supportsRemove) {
1579       try {
1580         values.retainAll(null);
1581         // Returning successfully is not ideal, but tolerated.
1582       } catch (NullPointerException expected) {
1583       }
1584     } else {
1585       try {
1586         values.retainAll(null);
1587         // We have to tolerate a successful return (Sun bug 4802647)
1588       } catch (UnsupportedOperationException | NullPointerException e) {
1589         // Expected.
1590       }
1591     }
1592     assertInvariants(map);
1593   }
1594 
testValuesClear()1595   public void testValuesClear() {
1596     final Map<K, V> map;
1597     try {
1598       map = makePopulatedMap();
1599     } catch (UnsupportedOperationException e) {
1600       return;
1601     }
1602 
1603     Collection<V> valueCollection = map.values();
1604     if (supportsClear) {
1605       valueCollection.clear();
1606       assertTrue(valueCollection.isEmpty());
1607     } else {
1608       try {
1609         valueCollection.clear();
1610         fail("Expected UnsupportedOperationException.");
1611       } catch (UnsupportedOperationException expected) {
1612       }
1613     }
1614     assertInvariants(map);
1615   }
1616 
mapEntry(K key, V value)1617   static <K, V> Entry<K, V> mapEntry(K key, V value) {
1618     return Collections.singletonMap(key, value).entrySet().iterator().next();
1619   }
1620 }
1621