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