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