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