• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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           int unused = 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     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     Map<K, V> map;
290     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     Map<K, V> map;
316     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     Map<K, V> map;
338     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     K unmappedKey;
348     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     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     Map<K, V> map;
373     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     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     Map<K, V> map;
400     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     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     Map<K, V> map;
424     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     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     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       iterator.next();
481       try {
482         iterator.remove();
483         fail("Expected UnsupportedOperationException.");
484       } catch (UnsupportedOperationException expected) {
485       }
486     }
487     assertInvariants(map);
488   }
489 
testEntrySetRemove()490   public void testEntrySetRemove() {
491     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     Map<K, V> map;
516     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     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     Map<K, V> map;
574     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     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     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     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     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     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     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 tolerated) {
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     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     Map<K, V> map = makeEitherMap();
769 
770     Set<Entry<K, V>> entrySet = map.entrySet();
771     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     Map<K, V> map;
797     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     V oldValue = entry.getValue();
808     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     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     V oldValue = entry.getValue();
832     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     Map<K, V> map;
841     try {
842       map = makePopulatedMap();
843     } catch (UnsupportedOperationException e) {
844       return;
845     }
846 
847     // Explicitly call `equals`; `assertEquals` might return fast
848     assertTrue(map.equals(map));
849     assertTrue(makePopulatedMap().equals(map));
850     assertFalse(map.equals(Collections.emptyMap()));
851     // no-inspection ObjectEqualsNull
852     assertFalse(map.equals(null));
853   }
854 
testEqualsForLargerMap()855   public void testEqualsForLargerMap() {
856     if (!supportsPut) {
857       return;
858     }
859 
860     Map<K, V> map;
861     Map<K, V> largerMap;
862     try {
863       map = makePopulatedMap();
864       largerMap = makePopulatedMap();
865       largerMap.put(getKeyNotInPopulatedMap(), getValueNotInPopulatedMap());
866     } catch (UnsupportedOperationException e) {
867       return;
868     }
869 
870     assertFalse(map.equals(largerMap));
871   }
872 
testEqualsForSmallerMap()873   public void testEqualsForSmallerMap() {
874     if (!supportsRemove) {
875       return;
876     }
877 
878     Map<K, V> map;
879     Map<K, V> smallerMap;
880     try {
881       map = makePopulatedMap();
882       smallerMap = makePopulatedMap();
883       smallerMap.remove(smallerMap.keySet().iterator().next());
884     } catch (UnsupportedOperationException e) {
885       return;
886     }
887 
888     assertFalse(map.equals(smallerMap));
889   }
890 
testEqualsForEmptyMap()891   public void testEqualsForEmptyMap() {
892     Map<K, V> map;
893     try {
894       map = makeEmptyMap();
895     } catch (UnsupportedOperationException e) {
896       return;
897     }
898 
899     // Explicitly call `equals`; `assertEquals` might return fast
900     assertTrue(map.equals(map));
901     assertTrue(makeEmptyMap().equals(map));
902     assertEquals(Collections.emptyMap(), map);
903     assertFalse(map.equals(Collections.emptySet()));
904     // noinspection ObjectEqualsNull
905     assertFalse(map.equals(null));
906   }
907 
testGet()908   public void testGet() {
909     Map<K, V> map;
910     try {
911       map = makePopulatedMap();
912     } catch (UnsupportedOperationException e) {
913       return;
914     }
915 
916     for (Entry<K, V> entry : map.entrySet()) {
917       assertEquals(entry.getValue(), map.get(entry.getKey()));
918     }
919 
920     K unmappedKey = null;
921     try {
922       unmappedKey = getKeyNotInPopulatedMap();
923     } catch (UnsupportedOperationException e) {
924       return;
925     }
926     assertNull(map.get(unmappedKey));
927   }
928 
testGetForEmptyMap()929   public void testGetForEmptyMap() {
930     Map<K, V> map;
931     K unmappedKey = null;
932     try {
933       map = makeEmptyMap();
934       unmappedKey = getKeyNotInPopulatedMap();
935     } catch (UnsupportedOperationException e) {
936       return;
937     }
938     assertNull(map.get(unmappedKey));
939   }
940 
testGetNull()941   public void testGetNull() {
942     Map<K, V> map = makeEitherMap();
943     if (allowsNullKeys) {
944       if (allowsNullValues) {
945         // TODO: decide what to test here.
946       } else {
947         assertEquals(map.containsKey(null), map.get(null) != null);
948       }
949     } else {
950       try {
951         map.get(null);
952       } catch (NullPointerException optional) {
953       }
954     }
955     assertInvariants(map);
956   }
957 
testHashCode()958   public void testHashCode() {
959     Map<K, V> map;
960     try {
961       map = makePopulatedMap();
962     } catch (UnsupportedOperationException e) {
963       return;
964     }
965     assertInvariants(map);
966   }
967 
testHashCodeForEmptyMap()968   public void testHashCodeForEmptyMap() {
969     Map<K, V> map;
970     try {
971       map = makeEmptyMap();
972     } catch (UnsupportedOperationException e) {
973       return;
974     }
975     assertInvariants(map);
976   }
977 
testPutNewKey()978   public void testPutNewKey() {
979     Map<K, V> map = makeEitherMap();
980     K keyToPut;
981     V valueToPut;
982     try {
983       keyToPut = getKeyNotInPopulatedMap();
984       valueToPut = getValueNotInPopulatedMap();
985     } catch (UnsupportedOperationException e) {
986       return;
987     }
988     if (supportsPut) {
989       int initialSize = map.size();
990       V oldValue = map.put(keyToPut, valueToPut);
991       assertEquals(valueToPut, map.get(keyToPut));
992       assertTrue(map.containsKey(keyToPut));
993       assertTrue(map.containsValue(valueToPut));
994       assertEquals(initialSize + 1, map.size());
995       assertNull(oldValue);
996     } else {
997       try {
998         map.put(keyToPut, valueToPut);
999         fail("Expected UnsupportedOperationException.");
1000       } catch (UnsupportedOperationException expected) {
1001       }
1002     }
1003     assertInvariants(map);
1004   }
1005 
testPutExistingKey()1006   public void testPutExistingKey() {
1007     Map<K, V> map;
1008     K keyToPut;
1009     V valueToPut;
1010     try {
1011       map = makePopulatedMap();
1012       valueToPut = getValueNotInPopulatedMap();
1013     } catch (UnsupportedOperationException e) {
1014       return;
1015     }
1016     keyToPut = map.keySet().iterator().next();
1017     if (supportsPut) {
1018       int initialSize = map.size();
1019       map.put(keyToPut, valueToPut);
1020       assertEquals(valueToPut, map.get(keyToPut));
1021       assertTrue(map.containsKey(keyToPut));
1022       assertTrue(map.containsValue(valueToPut));
1023       assertEquals(initialSize, map.size());
1024     } else {
1025       try {
1026         map.put(keyToPut, valueToPut);
1027         fail("Expected UnsupportedOperationException.");
1028       } catch (UnsupportedOperationException expected) {
1029       }
1030     }
1031     assertInvariants(map);
1032   }
1033 
testPutNullKey()1034   public void testPutNullKey() {
1035     if (!supportsPut) {
1036       return;
1037     }
1038     Map<K, V> map = makeEitherMap();
1039     V valueToPut;
1040     try {
1041       valueToPut = getValueNotInPopulatedMap();
1042     } catch (UnsupportedOperationException e) {
1043       return;
1044     }
1045     if (allowsNullKeys) {
1046       V oldValue = map.get(null);
1047       V returnedValue = map.put(null, valueToPut);
1048       assertEquals(oldValue, returnedValue);
1049       assertEquals(valueToPut, map.get(null));
1050       assertTrue(map.containsKey(null));
1051       assertTrue(map.containsValue(valueToPut));
1052     } else {
1053       try {
1054         map.put(null, valueToPut);
1055         fail("Expected RuntimeException");
1056       } catch (RuntimeException expected) {
1057       }
1058     }
1059     assertInvariants(map);
1060   }
1061 
testPutNullValue()1062   public void testPutNullValue() {
1063     if (!supportsPut) {
1064       return;
1065     }
1066     Map<K, V> map = makeEitherMap();
1067     K keyToPut;
1068     try {
1069       keyToPut = getKeyNotInPopulatedMap();
1070     } catch (UnsupportedOperationException e) {
1071       return;
1072     }
1073     if (allowsNullValues) {
1074       int initialSize = map.size();
1075       V oldValue = map.get(keyToPut);
1076       V returnedValue = map.put(keyToPut, null);
1077       assertEquals(oldValue, returnedValue);
1078       assertNull(map.get(keyToPut));
1079       assertTrue(map.containsKey(keyToPut));
1080       assertTrue(map.containsValue(null));
1081       assertEquals(initialSize + 1, map.size());
1082     } else {
1083       try {
1084         map.put(keyToPut, null);
1085         fail("Expected RuntimeException");
1086       } catch (RuntimeException expected) {
1087       }
1088     }
1089     assertInvariants(map);
1090   }
1091 
testPutNullValueForExistingKey()1092   public void testPutNullValueForExistingKey() {
1093     if (!supportsPut) {
1094       return;
1095     }
1096     Map<K, V> map;
1097     K keyToPut;
1098     try {
1099       map = makePopulatedMap();
1100       keyToPut = map.keySet().iterator().next();
1101     } catch (UnsupportedOperationException e) {
1102       return;
1103     }
1104     if (allowsNullValues) {
1105       int initialSize = map.size();
1106       V oldValue = map.get(keyToPut);
1107       V returnedValue = map.put(keyToPut, null);
1108       assertEquals(oldValue, returnedValue);
1109       assertNull(map.get(keyToPut));
1110       assertTrue(map.containsKey(keyToPut));
1111       assertTrue(map.containsValue(null));
1112       assertEquals(initialSize, map.size());
1113     } else {
1114       try {
1115         map.put(keyToPut, null);
1116         fail("Expected RuntimeException");
1117       } catch (RuntimeException expected) {
1118       }
1119     }
1120     assertInvariants(map);
1121   }
1122 
testPutAllNewKey()1123   public void testPutAllNewKey() {
1124     Map<K, V> map = makeEitherMap();
1125     K keyToPut;
1126     V valueToPut;
1127     try {
1128       keyToPut = getKeyNotInPopulatedMap();
1129       valueToPut = getValueNotInPopulatedMap();
1130     } catch (UnsupportedOperationException e) {
1131       return;
1132     }
1133     Map<K, V> mapToPut = Collections.singletonMap(keyToPut, valueToPut);
1134     if (supportsPut) {
1135       int initialSize = map.size();
1136       map.putAll(mapToPut);
1137       assertEquals(valueToPut, map.get(keyToPut));
1138       assertTrue(map.containsKey(keyToPut));
1139       assertTrue(map.containsValue(valueToPut));
1140       assertEquals(initialSize + 1, map.size());
1141     } else {
1142       try {
1143         map.putAll(mapToPut);
1144         fail("Expected UnsupportedOperationException.");
1145       } catch (UnsupportedOperationException expected) {
1146       }
1147     }
1148     assertInvariants(map);
1149   }
1150 
testPutAllExistingKey()1151   public void testPutAllExistingKey() {
1152     Map<K, V> map;
1153     K keyToPut;
1154     V valueToPut;
1155     try {
1156       map = makePopulatedMap();
1157       valueToPut = getValueNotInPopulatedMap();
1158     } catch (UnsupportedOperationException e) {
1159       return;
1160     }
1161     keyToPut = map.keySet().iterator().next();
1162     Map<K, V> mapToPut = Collections.singletonMap(keyToPut, valueToPut);
1163     int initialSize = map.size();
1164     if (supportsPut) {
1165       map.putAll(mapToPut);
1166       assertEquals(valueToPut, map.get(keyToPut));
1167       assertTrue(map.containsKey(keyToPut));
1168       assertTrue(map.containsValue(valueToPut));
1169     } else {
1170       try {
1171         map.putAll(mapToPut);
1172         fail("Expected UnsupportedOperationException.");
1173       } catch (UnsupportedOperationException expected) {
1174       }
1175     }
1176     assertEquals(initialSize, map.size());
1177     assertInvariants(map);
1178   }
1179 
testRemove()1180   public void testRemove() {
1181     Map<K, V> map;
1182     K keyToRemove;
1183     try {
1184       map = makePopulatedMap();
1185     } catch (UnsupportedOperationException e) {
1186       return;
1187     }
1188     keyToRemove = map.keySet().iterator().next();
1189     if (supportsRemove) {
1190       int initialSize = map.size();
1191       V expectedValue = map.get(keyToRemove);
1192       V oldValue = map.remove(keyToRemove);
1193       assertEquals(expectedValue, oldValue);
1194       assertFalse(map.containsKey(keyToRemove));
1195       assertEquals(initialSize - 1, map.size());
1196     } else {
1197       try {
1198         map.remove(keyToRemove);
1199         fail("Expected UnsupportedOperationException.");
1200       } catch (UnsupportedOperationException expected) {
1201       }
1202     }
1203     assertInvariants(map);
1204   }
1205 
testRemoveMissingKey()1206   public void testRemoveMissingKey() {
1207     Map<K, V> map;
1208     K keyToRemove;
1209     try {
1210       map = makePopulatedMap();
1211       keyToRemove = getKeyNotInPopulatedMap();
1212     } catch (UnsupportedOperationException e) {
1213       return;
1214     }
1215     if (supportsRemove) {
1216       int initialSize = map.size();
1217       assertNull(map.remove(keyToRemove));
1218       assertEquals(initialSize, map.size());
1219     } else {
1220       try {
1221         map.remove(keyToRemove);
1222         fail("Expected UnsupportedOperationException.");
1223       } catch (UnsupportedOperationException expected) {
1224       }
1225     }
1226     assertInvariants(map);
1227   }
1228 
testSize()1229   public void testSize() {
1230     assertInvariants(makeEitherMap());
1231   }
1232 
testKeySetRemove()1233   public void testKeySetRemove() {
1234     Map<K, V> map;
1235     try {
1236       map = makePopulatedMap();
1237     } catch (UnsupportedOperationException e) {
1238       return;
1239     }
1240 
1241     Set<K> keys = map.keySet();
1242     K key = keys.iterator().next();
1243     if (supportsRemove) {
1244       int initialSize = map.size();
1245       keys.remove(key);
1246       assertEquals(initialSize - 1, map.size());
1247       assertFalse(map.containsKey(key));
1248     } else {
1249       try {
1250         keys.remove(key);
1251         fail("Expected UnsupportedOperationException.");
1252       } catch (UnsupportedOperationException expected) {
1253       }
1254     }
1255     assertInvariants(map);
1256   }
1257 
testKeySetRemoveAll()1258   public void testKeySetRemoveAll() {
1259     Map<K, V> map;
1260     try {
1261       map = makePopulatedMap();
1262     } catch (UnsupportedOperationException e) {
1263       return;
1264     }
1265 
1266     Set<K> keys = map.keySet();
1267     K key = keys.iterator().next();
1268     if (supportsRemove) {
1269       int initialSize = map.size();
1270       assertTrue(keys.removeAll(Collections.singleton(key)));
1271       assertEquals(initialSize - 1, map.size());
1272       assertFalse(map.containsKey(key));
1273     } else {
1274       try {
1275         keys.removeAll(Collections.singleton(key));
1276         fail("Expected UnsupportedOperationException.");
1277       } catch (UnsupportedOperationException expected) {
1278       }
1279     }
1280     assertInvariants(map);
1281   }
1282 
testKeySetRetainAll()1283   public void testKeySetRetainAll() {
1284     Map<K, V> map;
1285     try {
1286       map = makePopulatedMap();
1287     } catch (UnsupportedOperationException e) {
1288       return;
1289     }
1290 
1291     Set<K> keys = map.keySet();
1292     K key = keys.iterator().next();
1293     if (supportsRemove) {
1294       keys.retainAll(Collections.singleton(key));
1295       assertEquals(1, map.size());
1296       assertTrue(map.containsKey(key));
1297     } else {
1298       try {
1299         keys.retainAll(Collections.singleton(key));
1300         fail("Expected UnsupportedOperationException.");
1301       } catch (UnsupportedOperationException expected) {
1302       }
1303     }
1304     assertInvariants(map);
1305   }
1306 
testKeySetClear()1307   public void testKeySetClear() {
1308     Map<K, V> map;
1309     try {
1310       map = makeEitherMap();
1311     } catch (UnsupportedOperationException e) {
1312       return;
1313     }
1314 
1315     Set<K> keySet = map.keySet();
1316     if (supportsClear) {
1317       keySet.clear();
1318       assertTrue(keySet.isEmpty());
1319     } else {
1320       try {
1321         keySet.clear();
1322         fail("Expected UnsupportedOperationException.");
1323       } catch (UnsupportedOperationException expected) {
1324       }
1325     }
1326     assertInvariants(map);
1327   }
1328 
testKeySetRemoveAllNullFromEmpty()1329   public void testKeySetRemoveAllNullFromEmpty() {
1330     Map<K, V> map;
1331     try {
1332       map = makeEmptyMap();
1333     } catch (UnsupportedOperationException e) {
1334       return;
1335     }
1336 
1337     Set<K> keySet = map.keySet();
1338     if (supportsRemove) {
1339       try {
1340         keySet.removeAll(null);
1341         fail("Expected NullPointerException.");
1342       } catch (NullPointerException expected) {
1343       }
1344     } else {
1345       try {
1346         keySet.removeAll(null);
1347         fail("Expected UnsupportedOperationException or NullPointerException.");
1348       } catch (UnsupportedOperationException | NullPointerException e) {
1349         // Expected.
1350       }
1351     }
1352     assertInvariants(map);
1353   }
1354 
testKeySetRetainAllNullFromEmpty()1355   public void testKeySetRetainAllNullFromEmpty() {
1356     Map<K, V> map;
1357     try {
1358       map = makeEmptyMap();
1359     } catch (UnsupportedOperationException e) {
1360       return;
1361     }
1362 
1363     Set<K> keySet = map.keySet();
1364     if (supportsRemove) {
1365       try {
1366         keySet.retainAll(null);
1367         // Returning successfully is not ideal, but tolerated.
1368       } catch (NullPointerException tolerated) {
1369       }
1370     } else {
1371       try {
1372         keySet.retainAll(null);
1373         // We have to tolerate a successful return (Sun bug 4802647)
1374       } catch (UnsupportedOperationException | NullPointerException e) {
1375         // Expected.
1376       }
1377     }
1378     assertInvariants(map);
1379   }
1380 
testValues()1381   public void testValues() {
1382     Map<K, V> map;
1383     Collection<V> valueCollection;
1384     try {
1385       map = makePopulatedMap();
1386     } catch (UnsupportedOperationException e) {
1387       return;
1388     }
1389     assertInvariants(map);
1390 
1391     valueCollection = map.values();
1392     V unmappedValue;
1393     try {
1394       unmappedValue = getValueNotInPopulatedMap();
1395     } catch (UnsupportedOperationException e) {
1396       return;
1397     }
1398     for (V value : valueCollection) {
1399       assertFalse(unmappedValue.equals(value));
1400     }
1401   }
1402 
testValuesIteratorRemove()1403   public void testValuesIteratorRemove() {
1404     Map<K, V> map;
1405     try {
1406       map = makePopulatedMap();
1407     } catch (UnsupportedOperationException e) {
1408       return;
1409     }
1410 
1411     Collection<V> valueCollection = map.values();
1412     Iterator<V> iterator = valueCollection.iterator();
1413     if (supportsIteratorRemove) {
1414       int initialSize = map.size();
1415       iterator.next();
1416       iterator.remove();
1417       assertEquals(initialSize - 1, map.size());
1418       // (We can't assert that the values collection no longer contains the
1419       // removed value, because the underlying map can have multiple mappings
1420       // to the same value.)
1421       assertInvariants(map);
1422       try {
1423         iterator.remove();
1424         fail("Expected IllegalStateException.");
1425       } catch (IllegalStateException expected) {
1426       }
1427     } else {
1428       iterator.next();
1429       try {
1430         iterator.remove();
1431         fail("Expected UnsupportedOperationException.");
1432       } catch (UnsupportedOperationException expected) {
1433       }
1434     }
1435     assertInvariants(map);
1436   }
1437 
testValuesRemove()1438   public void testValuesRemove() {
1439     Map<K, V> map;
1440     try {
1441       map = makePopulatedMap();
1442     } catch (UnsupportedOperationException e) {
1443       return;
1444     }
1445 
1446     Collection<V> valueCollection = map.values();
1447     if (supportsRemove) {
1448       int initialSize = map.size();
1449       valueCollection.remove(valueCollection.iterator().next());
1450       assertEquals(initialSize - 1, map.size());
1451       // (We can't assert that the values collection no longer contains the
1452       // removed value, because the underlying map can have multiple mappings
1453       // to the same value.)
1454     } else {
1455       try {
1456         valueCollection.remove(valueCollection.iterator().next());
1457         fail("Expected UnsupportedOperationException.");
1458       } catch (UnsupportedOperationException expected) {
1459       }
1460     }
1461     assertInvariants(map);
1462   }
1463 
testValuesRemoveMissing()1464   public void testValuesRemoveMissing() {
1465     Map<K, V> map;
1466     V valueToRemove;
1467     try {
1468       map = makeEitherMap();
1469       valueToRemove = getValueNotInPopulatedMap();
1470     } catch (UnsupportedOperationException e) {
1471       return;
1472     }
1473 
1474     Collection<V> valueCollection = map.values();
1475     int initialSize = map.size();
1476     if (supportsRemove) {
1477       assertFalse(valueCollection.remove(valueToRemove));
1478     } else {
1479       try {
1480         assertFalse(valueCollection.remove(valueToRemove));
1481       } catch (UnsupportedOperationException e) {
1482         // Tolerated.
1483       }
1484     }
1485     assertEquals(initialSize, map.size());
1486     assertInvariants(map);
1487   }
1488 
testValuesRemoveAll()1489   public void testValuesRemoveAll() {
1490     Map<K, V> map;
1491     try {
1492       map = makePopulatedMap();
1493     } catch (UnsupportedOperationException e) {
1494       return;
1495     }
1496 
1497     Collection<V> valueCollection = map.values();
1498     Set<V> valuesToRemove = singleton(valueCollection.iterator().next());
1499     if (supportsRemove) {
1500       valueCollection.removeAll(valuesToRemove);
1501       for (V value : valuesToRemove) {
1502         assertFalse(valueCollection.contains(value));
1503       }
1504       for (V value : valueCollection) {
1505         assertFalse(valuesToRemove.contains(value));
1506       }
1507     } else {
1508       try {
1509         valueCollection.removeAll(valuesToRemove);
1510         fail("Expected UnsupportedOperationException.");
1511       } catch (UnsupportedOperationException expected) {
1512       }
1513     }
1514     assertInvariants(map);
1515   }
1516 
testValuesRemoveAllNullFromEmpty()1517   public void testValuesRemoveAllNullFromEmpty() {
1518     Map<K, V> map;
1519     try {
1520       map = makeEmptyMap();
1521     } catch (UnsupportedOperationException e) {
1522       return;
1523     }
1524 
1525     Collection<V> values = map.values();
1526     if (supportsRemove) {
1527       try {
1528         values.removeAll(null);
1529         // Returning successfully is not ideal, but tolerated.
1530       } catch (NullPointerException tolerated) {
1531       }
1532     } else {
1533       try {
1534         values.removeAll(null);
1535         // We have to tolerate a successful return (Sun bug 4802647)
1536       } catch (UnsupportedOperationException | NullPointerException e) {
1537         // Expected.
1538       }
1539     }
1540     assertInvariants(map);
1541   }
1542 
testValuesRetainAll()1543   public void testValuesRetainAll() {
1544     Map<K, V> map;
1545     try {
1546       map = makePopulatedMap();
1547     } catch (UnsupportedOperationException e) {
1548       return;
1549     }
1550 
1551     Collection<V> valueCollection = map.values();
1552     Set<V> valuesToRetain = singleton(valueCollection.iterator().next());
1553     if (supportsRemove) {
1554       valueCollection.retainAll(valuesToRetain);
1555       for (V value : valuesToRetain) {
1556         assertTrue(valueCollection.contains(value));
1557       }
1558       for (V value : valueCollection) {
1559         assertTrue(valuesToRetain.contains(value));
1560       }
1561     } else {
1562       try {
1563         valueCollection.retainAll(valuesToRetain);
1564         fail("Expected UnsupportedOperationException.");
1565       } catch (UnsupportedOperationException expected) {
1566       }
1567     }
1568     assertInvariants(map);
1569   }
1570 
testValuesRetainAllNullFromEmpty()1571   public void testValuesRetainAllNullFromEmpty() {
1572     Map<K, V> map;
1573     try {
1574       map = makeEmptyMap();
1575     } catch (UnsupportedOperationException e) {
1576       return;
1577     }
1578 
1579     Collection<V> values = map.values();
1580     if (supportsRemove) {
1581       try {
1582         values.retainAll(null);
1583         // Returning successfully is not ideal, but tolerated.
1584       } catch (NullPointerException tolerated) {
1585       }
1586     } else {
1587       try {
1588         values.retainAll(null);
1589         // We have to tolerate a successful return (Sun bug 4802647)
1590       } catch (UnsupportedOperationException | NullPointerException e) {
1591         // Expected.
1592       }
1593     }
1594     assertInvariants(map);
1595   }
1596 
testValuesClear()1597   public void testValuesClear() {
1598     Map<K, V> map;
1599     try {
1600       map = makePopulatedMap();
1601     } catch (UnsupportedOperationException e) {
1602       return;
1603     }
1604 
1605     Collection<V> valueCollection = map.values();
1606     if (supportsClear) {
1607       valueCollection.clear();
1608       assertTrue(valueCollection.isEmpty());
1609     } else {
1610       try {
1611         valueCollection.clear();
1612         fail("Expected UnsupportedOperationException.");
1613       } catch (UnsupportedOperationException expected) {
1614       }
1615     }
1616     assertInvariants(map);
1617   }
1618 
mapEntry(K key, V value)1619   static <K, V> Entry<K, V> mapEntry(K key, V value) {
1620     return Collections.singletonMap(key, value).entrySet().iterator().next();
1621   }
1622 }
1623