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