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