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