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