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