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