1 /*
2  * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.
8  *
9  * This code is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12  * version 2 for more details (a copy is included in the LICENSE file that
13  * accompanied this code).
14  *
15  * You should have received a copy of the GNU General Public License version
16  * 2 along with this work; if not, write to the Free Software Foundation,
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18  *
19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20  * or visit www.oracle.com if you need additional information or have any
21  * questions.
22  */
23 
24 package test.java.util.SequencedCollection;
25 
26 import java.io.*;
27 import java.util.*;
28 
29 import org.testng.annotations.DataProvider;
30 import org.testng.annotations.Test;
31 
32 import static org.testng.Assert.assertEquals;
33 import static org.testng.Assert.assertNull;
34 import static org.testng.Assert.assertThrows;
35 import static org.testng.Assert.assertFalse;
36 import static org.testng.Assert.assertSame;
37 import static org.testng.Assert.assertTrue;
38 
39 /*
40  * @test
41  * @bug     8266571
42  * @summary Basic tests for SequencedMap
43  * @modules java.base/java.util:open
44  * @build   SimpleSortedMap
45  * @run     testng BasicMap
46  */
47 
48 public class BasicMap {
49 
50     // ========== Data Providers ==========
51 
52     static final Class<? extends Throwable> CCE  = ClassCastException.class;
53     static final Class<? extends Throwable> NSEE = NoSuchElementException.class;
54     static final Class<? extends Throwable> UOE  = UnsupportedOperationException.class;
55 
56     static final List<Map.Entry<String, Integer>> ORIGINAL =
57         List.of(Map.entry("a", 1),
58                 Map.entry("b", 2),
59                 Map.entry("c", 3),
60                 Map.entry("d", 4),
61                 Map.entry("e", 5));
62 
63     static <M extends SequencedMap<String, Integer>>
load(M map, List<Map.Entry<String, Integer>> mappings)64     M load(M map, List<Map.Entry<String, Integer>> mappings) {
65         for (var e : mappings)
66             map.put(e.getKey(), e.getValue());
67         return map;
68     }
69 
cknav(NavigableMap<String, Integer> map)70     static NavigableMap<String, Integer> cknav(NavigableMap<String, Integer> map) {
71         return Collections.checkedNavigableMap(map, String.class, Integer.class);
72     }
73 
cksorted(SortedMap<String, Integer> map)74     static SortedMap<String, Integer> cksorted(SortedMap<String, Integer> map) {
75         return Collections.checkedSortedMap(map, String.class, Integer.class);
76     }
77 
umap(SequencedMap<String, Integer> map)78     static SequencedMap<String, Integer> umap(SequencedMap<String, Integer> map) {
79         return Collections.unmodifiableSequencedMap(map);
80     }
81 
usorted(SortedMap<String, Integer> map)82     static SortedMap<String, Integer> usorted(SortedMap<String, Integer> map) {
83         return Collections.unmodifiableSortedMap(map);
84     }
85 
unav(NavigableMap<String, Integer> map)86     static NavigableMap<String, Integer> unav(NavigableMap<String, Integer> map) {
87         return Collections.unmodifiableNavigableMap(map);
88     }
89 
90     @DataProvider(name="all")
all()91     public Iterator<Object[]> all() {
92         var result = new ArrayList<Object[]>();
93         populated().forEachRemaining(result::add);
94         empties().forEachRemaining(result::add);
95         return result.iterator();
96     }
97 
98     @DataProvider(name="populated")
populated()99     public Iterator<Object[]> populated() {
100         return Arrays.asList(
101             new Object[] { "LinkedHashMap", load(new LinkedHashMap<>(), ORIGINAL), ORIGINAL },
102             new Object[] { "SimpleSortedMap", load(new SimpleSortedMap<>(), ORIGINAL), ORIGINAL },
103             new Object[] { "TreeMap", load(new TreeMap<>(), ORIGINAL), ORIGINAL },
104             new Object[] { "UnmodMap", umap(load(new LinkedHashMap<>(), ORIGINAL)), ORIGINAL }
105         ).iterator();
106     }
107 
108     @DataProvider(name="empties")
empties()109     public Iterator<Object[]> empties() {
110         return Arrays.asList(
111             new Object[] { "EmptyNavigableMap", Collections.emptyNavigableMap(), List.of() },
112             new Object[] { "EmptySortedMap", Collections.emptySortedMap(), List.of() },
113             new Object[] { "LinkedHashMap", new LinkedHashMap<>(), List.of() },
114             new Object[] { "SimpleSortedMap", new SimpleSortedMap<>(), List.of() },
115             new Object[] { "TreeMap", new TreeMap<>(), List.of() },
116             new Object[] { "UnmodMap", umap(new LinkedHashMap<>()), List.of() }
117         ).iterator();
118     }
119 
120     @DataProvider(name="polls")
polls()121     public Iterator<Object[]> polls() {
122         return Arrays.asList(
123             new Object[] { "LinkedHashMap", load(new LinkedHashMap<>(), ORIGINAL), ORIGINAL },
124             new Object[] { "SimpleSortedMap", load(new SimpleSortedMap<>(), ORIGINAL), ORIGINAL },
125             new Object[] { "TreeMap", load(new TreeMap<>(), ORIGINAL), ORIGINAL }
126         ).iterator();
127     }
128 
129     @DataProvider(name="emptyPolls")
emptyPolls()130     public Iterator<Object[]> emptyPolls() {
131         return Arrays.asList(
132             new Object[] { "LinkedHashMap", new LinkedHashMap<>(), List.of() },
133             new Object[] { "SimpleSortedMap", new SimpleSortedMap<>(), List.of() },
134             new Object[] { "TreeMap", new TreeMap<>(), List.of() }
135         ).iterator();
136     }
137 
138     @DataProvider(name="puts")
puts()139     public Iterator<Object[]> puts() {
140         return Arrays.<Object[]>asList(
141             new Object[] { "LinkedHashMap", load(new LinkedHashMap<>(), ORIGINAL), ORIGINAL }
142         ).iterator();
143     }
144 
145     @DataProvider(name="putUnpositioned")
putUnpositioned()146     public Iterator<Object[]> putUnpositioned() {
147         return Arrays.asList(
148             new Object[] { "LinkedHashMap", false, load(new LinkedHashMap<>(), ORIGINAL), ORIGINAL },
149             new Object[] { "LinkedHashMap", true,  load(new LinkedHashMap<>(), ORIGINAL), ORIGINAL }
150         ).iterator();
151     }
152 
153     @DataProvider(name="putThrows")
putThrows()154     public Iterator<Object[]> putThrows() {
155         return Arrays.asList(
156             new Object[] { "SimpleSortedMap", load(new SimpleSortedMap<>(), ORIGINAL), ORIGINAL },
157             new Object[] { "TreeMap", load(new TreeMap<>(), ORIGINAL), ORIGINAL }
158         ).iterator();
159     }
160 
161     @DataProvider(name="serializable")
serializable()162     public Iterator<Object[]> serializable() {
163         return Arrays.asList(
164             new Object[] { "LinkedHashMap", load(new LinkedHashMap<>(), ORIGINAL), ORIGINAL },
165             new Object[] { "TreeMap", load(new TreeMap<>(), ORIGINAL), ORIGINAL },
166             new Object[] { "UnmodMap", umap(load(new LinkedHashMap<>(), ORIGINAL)), ORIGINAL }
167         ).iterator();
168     }
169 
170     @DataProvider(name="notSerializable")
notSerializable()171     public Iterator<Object[]> notSerializable() {
172         return Arrays.asList(
173             new Object[] { "LinkedHashMap", load(new LinkedHashMap<>(), ORIGINAL).reversed() },
174             new Object[] { "UnmodMap", umap(load(new LinkedHashMap<>(), ORIGINAL)).reversed() }
175         ).iterator();
176     }
177 
178     @DataProvider(name="doubleReverse")
doubleReverse()179     public Iterator<Object[]> doubleReverse() {
180         return Arrays.<Object[]>asList(
181             new Object[] { "LinkedHashMap", load(new LinkedHashMap<>(), ORIGINAL) }
182         ).iterator();
183     }
184 
185     @DataProvider(name="unmodifiable")
unmodifible()186     public Iterator<Object[]> unmodifible() {
187         return Arrays.<Object[]>asList(
188             new Object[] { "UnmodMap", umap(load(new LinkedHashMap<>(), ORIGINAL)), ORIGINAL },
189             new Object[] { "UnmodNav", unav(load(new TreeMap<>(), ORIGINAL)), ORIGINAL },
190             new Object[] { "UnmodSorted", usorted(load(new TreeMap<>(), ORIGINAL)), ORIGINAL }
191         ).iterator();
192     }
193 
194     @DataProvider(name="checked")
checked()195     public Iterator<Object[]> checked() {
196         return Arrays.<Object[]>asList(
197             new Object[] { "ChkNav", cknav(load(new TreeMap<>(), ORIGINAL)), ORIGINAL },
198             new Object[] { "ChkSorted", cksorted(load(new TreeMap<>(), ORIGINAL)), ORIGINAL }
199         ).iterator();
200     }
201 
202     // mode bit tests
203 
reverseMap(int mode)204     boolean reverseMap(int mode)  { return (mode & 1) != 0; }
reverseView(int mode)205     boolean reverseView(int mode) { return (mode & 2) != 0; }
callLast(int mode)206     boolean callLast(int mode)    { return (mode & 4) != 0; }
207 
refLast(int mode)208     boolean refLast(int mode) { return reverseMap(mode) ^ reverseView(mode) ^ callLast(mode); }
209 
210     /**
211      * Generate cases for testing the removeFirst and removeLast methods of map views. For each
212      * different map implementation, generate 8 cases from the three bits of the testing mode
213      * int value:
214      *
215      *  (bit 1) if true, the backing map is reversed
216      *  (bit 2) if true, the view is reversed
217      *  (bit 4) if true, the last element of the view is to be removed, otherwise the first
218      *
219      * The three bits XORed together (by refLast(), above) indicate (if true) the last
220      * or (if false) the first element of the reference entry list is to be removed.
221      *
222      * @return the generated cases
223      */
224     @DataProvider(name="viewRemoves")
viewRemoves()225     public Iterator<Object[]> viewRemoves() {
226         var cases = new ArrayList<Object[]>();
227         for (int mode = 0; mode < 8; mode++) {
228             cases.addAll(Arrays.asList(
229                 new Object[] { "LinkedHashMap", mode, load(new LinkedHashMap<>(), ORIGINAL), ORIGINAL },
230                 new Object[] { "SimpleSortedMap", mode, load(new SimpleSortedMap<>(), ORIGINAL), ORIGINAL },
231                 new Object[] { "TreeMap", mode, load(new TreeMap<>(), ORIGINAL), ORIGINAL }
232             ));
233         }
234         return cases.iterator();
235     }
236 
237     @DataProvider(name="emptyViewRemoves")
emptyViewRemoves()238     public Iterator<Object[]> emptyViewRemoves() {
239         var cases = new ArrayList<Object[]>();
240         for (int mode = 0; mode < 8; mode++) {
241             cases.addAll(Arrays.asList(
242                 new Object[] { "LinkedHashMap", mode, new LinkedHashMap<>(), List.of() },
243                 new Object[] { "SimpleSortedMap", mode, new SimpleSortedMap<>(), List.of() },
244                 new Object[] { "TreeMap", mode, new TreeMap<>(), List.of() }
245             ));
246         }
247         return cases.iterator();
248     }
249 
250     @DataProvider(name="viewAddThrows")
viewAddThrows()251     public Iterator<Object[]> viewAddThrows() {
252         var cases = new ArrayList<Object[]>();
253         for (int mode = 0; mode < 8; mode++) {
254             cases.addAll(Arrays.asList(
255                 new Object[] { "LinkedHashMap", mode, load(new LinkedHashMap<>(), ORIGINAL), ORIGINAL },
256                 new Object[] { "SimpleSortedMap", mode, load(new SimpleSortedMap<>(), ORIGINAL), ORIGINAL },
257                 new Object[] { "TreeMap", mode, load(new TreeMap<>(), ORIGINAL), ORIGINAL }
258             ));
259         }
260         return cases.iterator();
261     }
262 
263     @DataProvider(name="nullableEntries")
nullableEntries()264     public Iterator<Object[]> nullableEntries() {
265         return Arrays.asList(
266             new Object[] { "firstEntry" },
267             new Object[] { "lastEntry" },
268             new Object[] { "pollFirstEntry" },
269             new Object[] { "pollLastEntry" }
270         ).iterator();
271     }
272 
273     // ========== Assertions ==========
274 
275     /**
276      * Basic checks over the contents of a SequencedMap, compared to a reference List of entries,
277      * in one direction.
278      *
279      * @param map the SequencedMap under test
280      * @param ref the reference list of entries
281      */
checkContents1(SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> ref)282     public void checkContents1(SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> ref) {
283         var list1 = new ArrayList<Map.Entry<String, Integer>>();
284         map.forEach((k, v) -> list1.add(Map.entry(k, v)));
285         assertEquals(list1, ref);
286 
287         assertEquals(map.size(), ref.size());
288         assertEquals(map.isEmpty(), ref.isEmpty());
289 
290         for (var e : ref) {
291             assertTrue(map.containsKey(e.getKey()));
292             assertTrue(map.containsValue(e.getValue()));
293             assertEquals(map.get(e.getKey()), e.getValue());
294         }
295     }
296 
checkContents(SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> ref)297     public void checkContents(SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> ref) {
298         checkContents1(map, ref);
299 
300         var rref = new ArrayList<>(ref);
301         Collections.reverse(rref);
302         var rmap = map.reversed();
303         checkContents1(rmap, rref);
304 
305         var rrmap = rmap.reversed();
306         checkContents1(rrmap, ref);
307     }
308 
309     /**
310      * Check the entrySet, keySet, or values view of a SequencedMap in one direction. The view
311      * collection is ordered even though the collection type is not sequenced.
312      *
313      * @param <T> the element type of the view
314      * @param mapView the actual map view
315      * @param expElements list of the expected elements
316      */
checkView1(Collection<T> mapView, List<T> expElements)317     public <T> void checkView1(Collection<T> mapView, List<T> expElements) {
318         var list1 = new ArrayList<T>();
319         for (var k : mapView)
320             list1.add(k);
321         assertEquals(list1, expElements);
322 
323         var list2 = new ArrayList<T>();
324         mapView.forEach(list2::add);
325         assertEquals(list2, expElements);
326 
327         var list3 = Arrays.asList(mapView.toArray());
328         assertEquals(list3, expElements);
329 
330         var list4 = Arrays.asList(mapView.toArray(new Object[0]));
331         assertEquals(list4, expElements);
332 
333         var list5 = Arrays.asList(mapView.toArray(Object[]::new));
334         assertEquals(list5, expElements);
335 
336         var list6 = mapView.stream().toList();
337         assertEquals(list6, expElements);
338 
339         var list7 = mapView.parallelStream().toList();
340         assertEquals(list7, expElements);
341 
342         assertEquals(mapView.size(), expElements.size());
343         assertEquals(mapView.isEmpty(), expElements.isEmpty());
344 
345         for (var k : expElements) {
346             assertTrue(mapView.contains(k));
347         }
348 
349         var it = mapView.iterator();
350         if (expElements.isEmpty()) {
351             assertFalse(it.hasNext());
352         } else {
353             assertTrue(it.hasNext());
354             assertEquals(it.next(), expElements.get(0));
355         }
356     }
357 
358     /**
359      * Check the sequenced entrySet, keySet, or values view of a SequencedMap in one direction.
360      *
361      * @param <T> the element type of the view
362      * @param mapView the actual map view
363      * @param expElements list of the expected elements
364      */
checkSeqView1(SequencedCollection<T> mapView, List<T> expElements)365     public <T> void checkSeqView1(SequencedCollection<T> mapView, List<T> expElements) {
366         checkView1(mapView, expElements);
367 
368         if (expElements.isEmpty()) {
369             assertThrows(NoSuchElementException.class, () -> mapView.getFirst());
370             assertThrows(NoSuchElementException.class, () -> mapView.getLast());
371         } else {
372             assertEquals(mapView.getFirst(), expElements.get(0));
373             assertEquals(mapView.getLast(), expElements.get(expElements.size() - 1));
374         }
375     }
376 
377     /**
378      * Check the keySet and sequencedKeySet views of a map. It's possible to unify this with
379      * the corresponding checks for values and entrySet views, but doing this adds a bunch
380      * of generics and method references that tend to obscure more than they help.
381      *
382      * @param map the SequencedMap under test
383      * @param refEntries expected contents of the map
384      */
checkKeySet(SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> refEntries)385     public void checkKeySet(SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> refEntries) {
386         List<String> refKeys = refEntries.stream().map(Map.Entry::getKey).toList();
387         List<String> rrefKeys = new ArrayList<>(refKeys);
388         Collections.reverse(rrefKeys);
389         SequencedMap<String, Integer> rmap = map.reversed();
390 
391         checkView1(map.keySet(), refKeys);
392         checkSeqView1(map.sequencedKeySet(), refKeys);
393         checkSeqView1(map.sequencedKeySet().reversed(), rrefKeys);
394 
395         checkView1(rmap.keySet(), rrefKeys);
396         checkSeqView1(rmap.sequencedKeySet(), rrefKeys);
397         checkSeqView1(rmap.sequencedKeySet().reversed(), refKeys);
398 
399         checkView1(rmap.reversed().keySet(), refKeys);
400         checkSeqView1(rmap.reversed().sequencedKeySet(), refKeys);
401         checkSeqView1(rmap.reversed().sequencedKeySet().reversed(), rrefKeys);
402 
403         assertEquals(map.keySet().hashCode(), rmap.keySet().hashCode());
404         assertEquals(map.keySet().hashCode(), map.sequencedKeySet().hashCode());
405         assertEquals(rmap.keySet().hashCode(), rmap.sequencedKeySet().hashCode());
406 
407         // Don't use assertEquals(), as we really want to test the equals() methods.
408         assertTrue(map.keySet().equals(map.sequencedKeySet()));
409         assertTrue(map.sequencedKeySet().equals(map.keySet()));
410         assertTrue(rmap.keySet().equals(map.sequencedKeySet()));
411         assertTrue(rmap.sequencedKeySet().equals(map.keySet()));
412         assertTrue(map.keySet().equals(rmap.sequencedKeySet()));
413         assertTrue(map.sequencedKeySet().equals(rmap.keySet()));
414         assertTrue(rmap.keySet().equals(rmap.sequencedKeySet()));
415         assertTrue(rmap.sequencedKeySet().equals(rmap.keySet()));
416     }
417 
418     /**
419      * Check the values and sequencedValues views of a map.
420      *
421      * @param map the SequencedMap under test
422      * @param refEntries expected contents of the map
423      */
checkValues(SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> refEntries)424     public void checkValues(SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> refEntries) {
425         List<Integer> refValues = refEntries.stream().map(Map.Entry::getValue).toList();
426         List<Integer> rrefValues = new ArrayList<>(refValues);
427         Collections.reverse(rrefValues);
428         SequencedMap<String, Integer> rmap = map.reversed();
429 
430         checkView1(map.values(), refValues);
431         checkSeqView1(map.sequencedValues(), refValues);
432         checkSeqView1(map.sequencedValues().reversed(), rrefValues);
433 
434         checkView1(rmap.values(), rrefValues);
435         checkSeqView1(rmap.sequencedValues(), rrefValues);
436         checkSeqView1(rmap.sequencedValues().reversed(), refValues);
437 
438         checkView1(rmap.reversed().values(), refValues);
439         checkSeqView1(rmap.reversed().sequencedValues(), refValues);
440         checkSeqView1(rmap.reversed().sequencedValues().reversed(), rrefValues);
441 
442         // No assertions over hashCode(), as Collection inherits Object.hashCode
443         // which is usually but not guaranteed to give unequal results.
444 
445         // It's permissible for an implementation to return the same instance for values()
446         // as for sequencedValues(). Either they're the same instance, or they must be
447         // unequal, because distinct collections should always be unequal.
448 
449         var v = map.values();
450         var sv = map.sequencedValues();
451         assertTrue((v == sv) || ! (v.equals(sv) || sv.equals(v)));
452 
453         var rv = rmap.values();
454         var rsv = rmap.sequencedValues();
455         assertTrue((rv == rsv) || ! (rv.equals(rsv) || rsv.equals(rv)));
456     }
457 
458     /**
459      * Check the entrySet and sequencedEntrySet views of a map.
460      *
461      * @param map the SequencedMap under test
462      * @param refEntries expected contents of the map
463      */
checkEntrySet(SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> refEntries)464     public void checkEntrySet(SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> refEntries) {
465         List<Map.Entry<String, Integer>> rref = new ArrayList<>(refEntries);
466         Collections.reverse(rref);
467         SequencedMap<String, Integer> rmap = map.reversed();
468 
469         checkView1(map.entrySet(), refEntries);
470         checkSeqView1(map.sequencedEntrySet(), refEntries);
471         checkSeqView1(map.sequencedEntrySet().reversed(), rref);
472 
473         checkView1(rmap.entrySet(), rref);
474         checkSeqView1(rmap.sequencedEntrySet(), rref);
475         checkSeqView1(rmap.sequencedEntrySet().reversed(), refEntries);
476 
477         checkView1(rmap.reversed().entrySet(), refEntries);
478         checkSeqView1(rmap.reversed().sequencedEntrySet(), refEntries);
479         checkSeqView1(rmap.reversed().sequencedEntrySet().reversed(), rref);
480 
481         assertEquals(map.entrySet().hashCode(), rmap.entrySet().hashCode());
482         assertEquals(map.entrySet().hashCode(), map.sequencedEntrySet().hashCode());
483         assertEquals(map.sequencedEntrySet().hashCode(), map.entrySet().hashCode());
484 
485         assertTrue(map.entrySet().equals(map.sequencedEntrySet()));
486         assertTrue(map.sequencedEntrySet().equals(map.entrySet()));
487         assertTrue(rmap.entrySet().equals(map.sequencedEntrySet()));
488         assertTrue(rmap.sequencedEntrySet().equals(map.entrySet()));
489         assertTrue(map.entrySet().equals(rmap.sequencedEntrySet()));
490         assertTrue(map.sequencedEntrySet().equals(rmap.entrySet()));
491         assertTrue(rmap.entrySet().equals(rmap.sequencedEntrySet()));
492         assertTrue(rmap.sequencedEntrySet().equals(rmap.entrySet()));
493     }
494 
495     /**
496      * Test attempted modifications to unmodifiable map views. The only mutating operation
497      * map views can support is removal.
498      *
499      * @param <T> element type of the map view
500      * @param view the map view
501      */
checkUnmodifiableView(Collection<T> view)502     public <T> void checkUnmodifiableView(Collection<T> view) {
503         assertThrows(UOE, () -> view.clear());
504         assertThrows(UOE, () -> { var it = view.iterator(); it.next(); it.remove(); });
505         assertThrows(UOE, () -> { var t = view.iterator().next(); view.remove(t); });
506 
507 // TODO these ops should throw unconditionally, but they don't in some implementations
508      // assertThrows(UOE, () -> view.removeAll(List.of()));
509      // assertThrows(UOE, () -> view.removeIf(x -> false));
510      // assertThrows(UOE, () -> view.retainAll(view));
511         assertThrows(UOE, () -> view.removeAll(view));
512         assertThrows(UOE, () -> view.removeIf(x -> true));
513         assertThrows(UOE, () -> view.retainAll(List.of()));
514     }
515 
516     /**
517      * Test removal methods on unmodifiable sequenced map views.
518      *
519      * @param <T> element type of the map view
520      * @param view the map view
521      */
checkUnmodifiableSeqView(SequencedCollection<T> view)522     public <T> void checkUnmodifiableSeqView(SequencedCollection<T> view) {
523         checkUnmodifiableView(view);
524         assertThrows(UOE, () -> view.removeFirst());
525         assertThrows(UOE, () -> view.removeLast());
526 
527         var rview = view.reversed();
528         checkUnmodifiableView(rview);
529         assertThrows(UOE, () -> rview.removeFirst());
530         assertThrows(UOE, () -> rview.removeLast());
531     }
532 
checkUnmodifiableEntry(SequencedMap<String, Integer> map)533     public void checkUnmodifiableEntry(SequencedMap<String, Integer> map) {
534         assertThrows(UOE, () -> { map.firstEntry().setValue(99); });
535         assertThrows(UOE, () -> { map.lastEntry().setValue(99); });
536         assertThrows(UOE, () -> { map.sequencedEntrySet().getFirst().setValue(99); });
537         assertThrows(UOE, () -> { map.sequencedEntrySet().getLast().setValue(99); });
538         assertThrows(UOE, () -> { map.sequencedEntrySet().reversed().getFirst().setValue(99); });
539         assertThrows(UOE, () -> { map.sequencedEntrySet().reversed().getLast().setValue(99); });
540     }
541 
checkUnmodifiable1(SequencedMap<String, Integer> map)542     public void checkUnmodifiable1(SequencedMap<String, Integer> map) {
543         assertThrows(UOE, () -> map.putFirst("x", 99));
544         assertThrows(UOE, () -> map.putLast("x", 99));
545         assertThrows(UOE, () -> { map.pollFirstEntry(); });
546         assertThrows(UOE, () -> { map.pollLastEntry(); });
547 
548         checkUnmodifiableEntry(map);
549         checkUnmodifiableView(map.keySet());
550         checkUnmodifiableView(map.values());
551         checkUnmodifiableView(map.entrySet());
552         checkUnmodifiableSeqView(map.sequencedKeySet());
553         checkUnmodifiableSeqView(map.sequencedValues());
554         checkUnmodifiableSeqView(map.sequencedEntrySet());
555     }
556 
checkUnmodifiable(SequencedMap<String, Integer> map)557     public void checkUnmodifiable(SequencedMap<String, Integer> map) {
558         checkUnmodifiable1(map);
559         checkUnmodifiable1(map.reversed());
560     }
561 
562     // The putFirst/putLast operations aren't tested here, because the only instances of
563     // checked, sequenced maps are SortedMap and NavigableMap, which don't support them.
checkChecked(SequencedMap<String, Integer> map)564     public void checkChecked(SequencedMap<String, Integer> map) {
565         SequencedMap<Object, Object> objMap = (SequencedMap<Object, Object>)(SequencedMap)map;
566         assertThrows(CCE, () -> { objMap.put(new Object(), 99); });
567         assertThrows(CCE, () -> { objMap.put("x", new Object()); });
568         assertThrows(CCE, () -> { objMap.sequencedEntrySet().getFirst().setValue(new Object()); });
569         assertThrows(CCE, () -> { objMap.sequencedEntrySet().reversed().getFirst().setValue(new Object()); });
570         assertThrows(CCE, () -> { objMap.reversed().put(new Object(), 99); });
571         assertThrows(CCE, () -> { objMap.reversed().put("x", new Object()); });
572         assertThrows(CCE, () -> { objMap.reversed().sequencedEntrySet().getFirst().setValue(new Object()); });
573         assertThrows(CCE, () -> { objMap.reversed().sequencedEntrySet().reversed().getFirst().setValue(new Object()); });
574     }
575 
checkEntry(Map.Entry<String, Integer> entry, String key, Integer value)576     public void checkEntry(Map.Entry<String, Integer> entry, String key, Integer value) {
577         assertEquals(entry.getKey(), key);
578         assertEquals(entry.getValue(), value);
579     }
580 
581     // ========== Tests ==========
582 
583     @Test(dataProvider="all")
testFundamentals(String label, SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> ref)584     public void testFundamentals(String label, SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> ref) {
585         checkContents(map, ref);
586         checkEntrySet(map, ref);
587         checkKeySet(map, ref);
588         checkValues(map, ref);
589     }
590 
591     @Test(dataProvider="populated")
testFirstEntry(String label, SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> ref)592     public void testFirstEntry(String label, SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> ref) {
593         assertEquals(map.firstEntry(), ref.get(0));
594         assertEquals(map.reversed().firstEntry(), ref.get(ref.size() - 1));
595         assertThrows(UOE, () -> { map.firstEntry().setValue(99); });
596         assertThrows(UOE, () -> { map.reversed().firstEntry().setValue(99); });
597         checkContents(map, ref);
598     }
599 
600     @Test(dataProvider="populated")
testLastEntry(String label, SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> ref)601     public void testLastEntry(String label, SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> ref) {
602         assertEquals(map.lastEntry(), ref.get(ref.size() - 1));
603         assertEquals(map.reversed().lastEntry(), ref.get(0));
604         assertThrows(UOE, () -> { map.lastEntry().setValue(99); });
605         assertThrows(UOE, () -> { map.reversed().lastEntry().setValue(99); });
606         checkContents(map, ref);
607     }
608 
609     @Test(dataProvider="empties")
testEmptyFirstEntry(String label, SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> ref)610     public void testEmptyFirstEntry(String label, SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> ref) {
611         assertNull(map.firstEntry());
612         assertNull(map.reversed().firstEntry());
613         checkContents(map, ref);
614     }
615 
616     @Test(dataProvider="empties")
testEmptyLastEntry(String label, SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> ref)617     public void testEmptyLastEntry(String label, SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> ref) {
618         assertNull(map.lastEntry());
619         assertNull(map.reversed().lastEntry());
620         checkContents(map, ref);
621     }
622 
623     @Test(dataProvider="puts")
testPutFirst(String label, SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> baseref)624     public void testPutFirst(String label, SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> baseref) {
625         var ref = new ArrayList<>(baseref);
626         ref.add(0, Map.entry("x", 99));
627         map.putFirst("x", 99);
628         checkContents(map, ref);
629     }
630 
631     @Test(dataProvider="puts")
testPutFirstRev(String label, SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> baseref)632     public void testPutFirstRev(String label, SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> baseref) {
633         var ref = new ArrayList<>(baseref);
634         ref.add(Map.entry("x", 99));
635         map.reversed().putFirst("x", 99);
636         checkContents(map, ref);
637     }
638 
639     @Test(dataProvider="puts")
testPutLast(String label, SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> baseref)640     public void testPutLast(String label, SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> baseref) {
641         var ref = new ArrayList<>(baseref);
642         ref.add(Map.entry("x", 99));
643         map.putLast("x", 99);
644         checkContents(map, ref);
645     }
646 
647     @Test(dataProvider="puts")
testPutLastRev(String label, SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> baseref)648     public void testPutLastRev(String label, SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> baseref) {
649         var ref = new ArrayList<>(baseref);
650         ref.add(0, Map.entry("x", 99));
651         map.reversed().putLast("x", 99);
652         checkContents(map, ref);
653     }
654 
655     @Test(dataProvider="putUnpositioned")
testUnposPut(String label, boolean rev, SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> baseref)656     public void testUnposPut(String label, boolean rev, SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> baseref) {
657         var ref = new ArrayList<>(baseref);
658         ref.add(Map.entry("x", 99));
659         (rev ? map.reversed() : map).put("x", 99);
660         checkContents(map, ref);
661     }
662 
663     @Test(dataProvider="putUnpositioned")
testUnposPutAll(String label, boolean rev, SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> baseref)664     public void testUnposPutAll(String label, boolean rev, SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> baseref) {
665         var ref = new ArrayList<>(baseref);
666         ref.add(Map.entry("x", 99));
667         (rev ? map.reversed() : map).putAll(Map.of("x", 99));
668         checkContents(map, ref);
669     }
670 
671     @Test(dataProvider="putUnpositioned")
testUnposPutIfAbsent(String label, boolean rev, SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> baseref)672     public void testUnposPutIfAbsent(String label, boolean rev, SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> baseref) {
673         var ref = new ArrayList<>(baseref);
674         ref.add(Map.entry("x", 99));
675         (rev ? map.reversed() : map).putIfAbsent("x", 99);
676         checkContents(map, ref);
677     }
678 
679     @Test(dataProvider="putUnpositioned")
testUnposCompute(String label, boolean rev, SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> baseref)680     public void testUnposCompute(String label, boolean rev, SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> baseref) {
681         var ref = new ArrayList<>(baseref);
682         ref.add(Map.entry("x", 99));
683         (rev ? map.reversed() : map).compute("x", (k, v) -> 99);
684         checkContents(map, ref);
685     }
686 
687     @Test(dataProvider="putUnpositioned")
testUnposComputeIfAbsent(String label, boolean rev, SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> baseref)688     public void testUnposComputeIfAbsent(String label, boolean rev, SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> baseref) {
689         var ref = new ArrayList<>(baseref);
690         ref.add(Map.entry("x", 99));
691         (rev ? map.reversed() : map).computeIfAbsent("x", k -> 99);
692         checkContents(map, ref);
693     }
694 
695     @Test(dataProvider="putUnpositioned")
testUnposMerge(String label, boolean rev, SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> baseref)696     public void testUnposMerge(String label, boolean rev, SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> baseref) {
697         var ref = new ArrayList<>(baseref);
698         ref.add(Map.entry("x", 99));
699         (rev ? map.reversed() : map).merge("x", 99, /*unused*/ (k, v) -> -1);
700         checkContents(map, ref);
701     }
702 
703     @Test(dataProvider="putThrows")
testPutThrows(String label, SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> baseref)704     public void testPutThrows(String label, SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> baseref) {
705         assertThrows(UOE, () -> map.putFirst("x", 99));
706         assertThrows(UOE, () -> map.putLast("x", 99));
707         assertThrows(UOE, () -> map.reversed().putFirst("x", 99));
708         assertThrows(UOE, () -> map.reversed().putLast("x", 99));
709         checkContents(map, baseref);
710     }
711 
712     @Test(dataProvider="polls")
testPollFirst(String label, SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> baseref)713     public void testPollFirst(String label, SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> baseref) {
714         var ref = new ArrayList<>(baseref);
715         var act = map.pollFirstEntry();
716         assertEquals(act, ref.remove(0));
717         assertThrows(UOE, () -> { act.setValue(99); });
718         checkContents(map, ref);
719     }
720 
721     @Test(dataProvider="polls")
testPollFirstRev(String label, SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> baseref)722     public void testPollFirstRev(String label, SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> baseref) {
723         var ref = new ArrayList<>(baseref);
724         var act = map.reversed().pollFirstEntry();
725         assertEquals(act, ref.remove(ref.size() - 1));
726         assertThrows(UOE, () -> { act.setValue(99); });
727         checkContents(map, ref);
728     }
729 
730     @Test(dataProvider="polls")
testPollLast(String label, SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> baseref)731     public void testPollLast(String label, SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> baseref) {
732         var ref = new ArrayList<>(baseref);
733         var act = map.pollLastEntry();
734         assertEquals(act, ref.remove(ref.size() - 1));
735         assertThrows(UOE, () -> { act.setValue(99); });
736         checkContents(map, ref);
737     }
738 
739     @Test(dataProvider="polls")
testPollLastRev(String label, SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> baseref)740     public void testPollLastRev(String label, SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> baseref) {
741         var ref = new ArrayList<>(baseref);
742         var act = map.reversed().pollLastEntry();
743         assertEquals(act, ref.remove(0));
744         assertThrows(UOE, () -> { act.setValue(99); });
745         checkContents(map, ref);
746     }
747 
748     @Test(dataProvider="emptyPolls")
testEmptyPollFirst(String label, SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> ref)749     public void testEmptyPollFirst(String label, SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> ref) {
750         assertNull(map.pollFirstEntry());
751         assertNull(map.reversed().pollFirstEntry());
752         checkContents(map, ref);
753     }
754 
755     @Test(dataProvider="emptyPolls")
testEmptyPollLast(String label, SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> ref)756     public void testEmptyPollLast(String label, SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> ref) {
757         assertNull(map.pollLastEntry());
758         assertNull(map.reversed().pollLastEntry());
759         checkContents(map, ref);
760     }
761 
762     @Test(dataProvider="serializable")
testSerializable(String label, SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> ref)763     public void testSerializable(String label, SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> ref)
764         throws ClassNotFoundException, IOException
765     {
766         var baos = new ByteArrayOutputStream();
767         try (var oos = new ObjectOutputStream(baos)) {
768             oos.writeObject(map);
769         }
770 
771         try (var bais = new ByteArrayInputStream(baos.toByteArray());
772              var ois = new ObjectInputStream(bais)) {
773             var map2 = (SequencedMap<String, Integer>) ois.readObject();
774             checkContents(map2, ref);
775         }
776     }
777 
778     @Test(dataProvider="notSerializable")
testNotSerializable(String label, SequencedMap<String, Integer> map)779     public void testNotSerializable(String label, SequencedMap<String, Integer> map)
780         throws ClassNotFoundException, IOException
781     {
782         var baos = new ByteArrayOutputStream();
783         try (var oos = new ObjectOutputStream(baos)) {
784             assertThrows(ObjectStreamException.class, () -> oos.writeObject(map));
785         }
786     }
787 
788     @Test(dataProvider="doubleReverse")
testDoubleReverse(String label, SequencedMap<String, Integer> map)789     public void testDoubleReverse(String label, SequencedMap<String, Integer> map) {
790         var rrmap = map.reversed().reversed();
791         assertSame(rrmap, map);
792     }
793 
794     @Test(dataProvider="unmodifiable")
testUnmodifiable(String label, SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> ref)795     public void testUnmodifiable(String label, SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> ref) {
796         checkUnmodifiable(map);
797         checkContents(map, ref);
798     }
799 
800     @Test(dataProvider="checked")
testChecked(String label, SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> ref)801     public void testChecked(String label, SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> ref) {
802         checkChecked(map);
803         checkContents(map, ref);
804     }
805 
806     /**
807      * Test that a removal from the sequenedKeySet view is properly reflected in the original
808      * backing map. The mode value indicates whether the backing map is reversed, whether the
809      * sequencedKeySet view is reversed, and whether the removeFirst or removeLast is called
810      * on the view. See the viewRemoves() dataProvider for details.
811      *
812      * @param label the implementation label
813      * @param mode reversed and first/last modes
814      * @param map the original map instance
815      * @param baseref reference contents of the original map
816      */
817     @Test(dataProvider="viewRemoves")
testKeySetRemoves(String label, int mode, SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> baseref)818     public void testKeySetRemoves(String label,
819                                   int mode,
820                                   SequencedMap<String, Integer> map,
821                                   List<Map.Entry<String, Integer>> baseref) {
822         var ref = new ArrayList<>(baseref);
823         var exp = (refLast(mode) ? ref.remove(ref.size() - 1) : ref.remove(0)).getKey();
824         var tempmap = reverseMap(mode) ? map.reversed() : map;
825         var keySet = reverseView(mode) ? tempmap.sequencedKeySet().reversed() : tempmap.sequencedKeySet();
826         var act = callLast(mode) ? keySet.removeLast() : keySet.removeFirst();
827         assertEquals(act, exp);
828         checkContents(map, ref);
829     }
830 
831     // As above, but for the sequencedValues view.
832     @Test(dataProvider="viewRemoves")
testValuesRemoves(String label, int mode, SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> baseref)833     public void testValuesRemoves(String label,
834                                   int mode,
835                                   SequencedMap<String, Integer> map,
836                                   List<Map.Entry<String, Integer>> baseref) {
837         var ref = new ArrayList<>(baseref);
838         var exp = (refLast(mode) ? ref.remove(ref.size() - 1) : ref.remove(0)).getValue();
839         var tempmap = reverseMap(mode) ? map.reversed() : map;
840         var values = reverseView(mode) ? tempmap.sequencedValues().reversed() : tempmap.sequencedValues();
841         var act = callLast(mode) ? values.removeLast() : values.removeFirst();
842         assertEquals(act, exp);
843         checkContents(map, ref);
844     }
845 
846     // As above, but for the sequencedEntrySet view.
847     @Test(dataProvider="viewRemoves")
testEntrySetRemoves(String label, int mode, SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> baseref)848     public void testEntrySetRemoves(String label,
849                                     int mode,
850                                     SequencedMap<String, Integer> map,
851                                     List<Map.Entry<String, Integer>> baseref) {
852         var ref = new ArrayList<>(baseref);
853         var exp = refLast(mode) ? ref.remove(ref.size() - 1) : ref.remove(0);
854         var tempmap = reverseMap(mode) ? map.reversed() : map;
855         var entrySet = reverseView(mode) ? tempmap.sequencedEntrySet().reversed() : tempmap.sequencedEntrySet();
856         var act = callLast(mode) ? entrySet.removeLast() : entrySet.removeFirst();
857         assertEquals(act, exp);
858         checkContents(map, ref);
859     }
860 
861     // As above, but for the sequencedKeySet of an empty map.
862     @Test(dataProvider="emptyViewRemoves")
testEmptyKeySetRemoves(String label, int mode, SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> baseref)863     public void testEmptyKeySetRemoves(String label,
864                                        int mode,
865                                        SequencedMap<String, Integer> map,
866                                        List<Map.Entry<String, Integer>> baseref) {
867         var tempmap = reverseMap(mode) ? map.reversed() : map;
868         var keySet = reverseView(mode) ? tempmap.sequencedKeySet().reversed() : tempmap.sequencedKeySet();
869         if (callLast(mode))
870             assertThrows(NSEE, () -> keySet.removeLast());
871         else
872             assertThrows(NSEE, () -> keySet.removeFirst());
873         checkContents(map, baseref);
874 
875     }
876 
877     // As above, but for the sequencedValues view.
878     @Test(dataProvider="emptyViewRemoves")
testEmptyValuesRemoves(String label, int mode, SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> baseref)879     public void testEmptyValuesRemoves(String label,
880                                        int mode,
881                                        SequencedMap<String, Integer> map,
882                                        List<Map.Entry<String, Integer>> baseref) {
883         var tempmap = reverseMap(mode) ? map.reversed() : map;
884         var values = reverseView(mode) ? tempmap.sequencedValues().reversed() : tempmap.sequencedValues();
885         if (callLast(mode))
886             assertThrows(NSEE, () -> values.removeLast());
887         else
888             assertThrows(NSEE, () -> values.removeFirst());
889         checkContents(map, baseref);
890     }
891 
892     // As above, but for the sequencedEntrySet view.
893     @Test(dataProvider="emptyViewRemoves")
testEmptyEntrySetRemoves(String label, int mode, SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> baseref)894     public void testEmptyEntrySetRemoves(String label,
895                                          int mode,
896                                          SequencedMap<String, Integer> map,
897                                          List<Map.Entry<String, Integer>> baseref) {
898         var tempmap = reverseMap(mode) ? map.reversed() : map;
899         var entrySet = reverseView(mode) ? tempmap.sequencedEntrySet().reversed() : tempmap.sequencedEntrySet();
900         if (callLast(mode))
901             assertThrows(NSEE, () -> entrySet.removeLast());
902         else
903             assertThrows(NSEE, () -> entrySet.removeFirst());
904         checkContents(map, baseref);
905     }
906 
907     // Test that addFirst/addLast on the sequencedKeySetView throw UnsupportedOperationException.
908     @Test(dataProvider="viewAddThrows")
testKeySetAddThrows(String label, int mode, SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> baseref)909     public void testKeySetAddThrows(String label,
910                                     int mode,
911                                     SequencedMap<String, Integer> map,
912                                     List<Map.Entry<String, Integer>> baseref) {
913         var tempmap = reverseMap(mode) ? map.reversed() : map;
914         var keySet = reverseView(mode) ? tempmap.sequencedKeySet().reversed() : tempmap.sequencedKeySet();
915         if (callLast(mode))
916             assertThrows(UOE, () -> keySet.addLast("x"));
917         else
918             assertThrows(UOE, () -> keySet.addFirst("x"));
919         checkContents(map, baseref);
920     }
921 
922     // As above, but for the sequencedValues view.
923     @Test(dataProvider="viewAddThrows")
testValuesAddThrows(String label, int mode, SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> baseref)924     public void testValuesAddThrows(String label,
925                                     int mode,
926                                     SequencedMap<String, Integer> map,
927                                     List<Map.Entry<String, Integer>> baseref) {
928         var tempmap = reverseMap(mode) ? map.reversed() : map;
929         var values = reverseView(mode) ? tempmap.sequencedValues().reversed() : tempmap.sequencedValues();
930         if (callLast(mode))
931             assertThrows(UOE, () -> values.addLast(99));
932         else
933             assertThrows(UOE, () -> values.addFirst(99));
934         checkContents(map, baseref);
935     }
936 
937     // As above, but for the sequencedEntrySet view.
938     @Test(dataProvider="viewAddThrows")
testEntrySetAddThrows(String label, int mode, SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> baseref)939     public void testEntrySetAddThrows(String label,
940                                       int mode,
941                                       SequencedMap<String, Integer> map,
942                                       List<Map.Entry<String, Integer>> baseref) {
943         var tempmap = reverseMap(mode) ? map.reversed() : map;
944         var entrySet = reverseView(mode) ? tempmap.sequencedEntrySet().reversed() : tempmap.sequencedEntrySet();
945         if (callLast(mode))
946             assertThrows(UOE, () -> entrySet.addLast(Map.entry("x", 99)));
947         else
948             assertThrows(UOE, () -> entrySet.addFirst(Map.entry("x", 99)));
949         checkContents(map, baseref);
950     }
951 
952     @Test(dataProvider="nullableEntries")
testNullableKeyValue(String mode)953     public void testNullableKeyValue(String mode) {
954         // TODO this relies on LHM to inherit SequencedMap default
955         // methods which are actually being tested here.
956         SequencedMap<String, Integer> map = new LinkedHashMap<>();
957         map.put(null, 1);
958         map.put("two", null);
959 
960         switch (mode) {
961             case "firstEntry"     -> checkEntry(map.firstEntry(), null, 1);
962             case "lastEntry"      -> checkEntry(map.lastEntry(), "two", null);
963             case "pollFirstEntry" -> checkEntry(map.pollFirstEntry(), null, 1);
964             case "pollLastEntry"  -> checkEntry(map.pollLastEntry(), "two", null);
965             default               -> throw new AssertionError("illegal mode " + mode);
966         }
967     }
968 }
969