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