Path: blob/master/test/jdk/java/util/NavigableMap/LockStep.java
41149 views
/*1* Copyright (c) 2006, 2018, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/2223/*24* @test25* @bug 6420753 6242436 669118526* @summary Compare NavigableMap implementations for identical behavior27* @run main LockStep28* @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:+EliminateAutoBox -XX:AutoBoxCacheMax=20000 LockStep29* @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:+EliminateAutoBox -XX:AutoBoxCacheMax=20000 -Dthorough=true LockStep30* @author Martin Buchholz31* @key randomness32*/3334import java.io.ByteArrayInputStream;35import java.io.ByteArrayOutputStream;36import java.io.IOException;37import java.io.InputStream;38import java.io.NotSerializableException;39import java.io.ObjectInputStream;40import java.io.ObjectOutputStream;41import java.io.Serializable;42import java.util.Arrays;43import java.util.Collection;44import java.util.Collections;45import java.util.Comparator;46import java.util.HashSet;47import java.util.Iterator;48import java.util.Map;49import java.util.NavigableMap;50import java.util.NavigableSet;51import java.util.NoSuchElementException;52import java.util.Objects;53import java.util.Random;54import java.util.Set;55import java.util.TreeMap;56import java.util.TreeSet;57import java.util.concurrent.ConcurrentSkipListMap;58import java.util.concurrent.ConcurrentSkipListSet;5960import static java.util.Collections.reverseOrder;61import static java.util.Collections.singleton;62import static java.util.Collections.singletonMap;6364@SuppressWarnings("unchecked")65public class LockStep {66static final int DEFAULT_SIZE = 5;67static int size; // Running time is O(size**2)6869static int intArg(String[] args, int i, int defaultValue) {70return args.length > i ? Integer.parseInt(args[i]) : defaultValue;71}7273// Pass -Dthorough=true for more exhaustive testing74static final boolean thorough = Boolean.getBoolean("thorough");7576static boolean maybe(int n) { return rnd.nextInt(n) == 0; }7778static void realMain(String[] args) {79size = intArg(args, 0, DEFAULT_SIZE);8081lockSteps(new TreeMap<>(),82new ConcurrentSkipListMap<>());83lockSteps(new TreeMap<>(),84Collections.checkedNavigableMap(new TreeMap<>(), Integer.class, Integer.class));85lockSteps(new TreeMap<>(),86Collections.synchronizedNavigableMap(new TreeMap<>()));87lockSteps(new TreeMap<>(reverseOrder()),88new ConcurrentSkipListMap<>(reverseOrder()));8990lockSteps(new TreeSet<>(),91new ConcurrentSkipListSet<>());92lockSteps(new TreeSet<>(),93Collections.checkedNavigableSet(new TreeSet<>(), Integer.class));94lockSteps(new TreeSet<>(),95Collections.synchronizedNavigableSet(new TreeSet<>()));96lockSteps(new TreeSet<>(reverseOrder()),97new ConcurrentSkipListSet<>(reverseOrder()));98}99100static void lockSteps(NavigableMap<Integer, Integer> m1, NavigableMap<Integer, Integer> m2) {101if (maybe(4)) m1 = serialClone(m1);102if (maybe(4)) m2 = serialClone(m2);103lockStep(m1,104m2);105lockStep(m1.descendingMap(),106m2.descendingMap());107lockStep(fullSubMap(m1),108fullSubMap(m2));109lockStep(fullSubMap(m1.descendingMap()),110fullSubMap(m2.descendingMap()));111lockStep(fullHeadMap(m1),112fullHeadMap(m2));113lockStep(fullHeadMap(m1.descendingMap()),114fullHeadMap(m2.descendingMap()));115lockStep(fullTailMap(m1),116fullTailMap(m2));117lockStep(fullTailMap(m1.descendingMap()),118fullTailMap(m2.descendingMap()));119}120121static void lockSteps(NavigableSet<Integer> s1, NavigableSet<Integer> s2) {122if (maybe(4)) s1 = serialClone(s1);123if (maybe(4)) s2 = serialClone(s2);124lockStep(s1,125s2);126lockStep(s1.descendingSet(),127s2.descendingSet());128lockStep(fullSubSet(s1),129fullSubSet(s2));130lockStep(fullSubSet(s1.descendingSet()),131fullSubSet(s2.descendingSet()));132lockStep(fullHeadSet(s1),133fullHeadSet(s2));134lockStep(fullHeadSet(s1.descendingSet()),135fullHeadSet(s2.descendingSet()));136lockStep(fullTailSet(s1),137fullTailSet(s2));138lockStep(fullTailSet(s1.descendingSet()),139fullTailSet(s2.descendingSet()));140}141142static boolean isAscending(NavigableMap<Integer, Integer> m) {143var cmp = m.comparator();144return (cmp == null || cmp.compare(1, 2) < 0);145}146147static NavigableMap<Integer, Integer> fullSubMap(NavigableMap<Integer, Integer> m) {148return isAscending(m)149? m.subMap(Integer.MIN_VALUE, true, Integer.MAX_VALUE, true)150: m.subMap(Integer.MAX_VALUE, true, Integer.MIN_VALUE, true);151}152153static NavigableMap<Integer, Integer> fullHeadMap(NavigableMap<Integer, Integer> m) {154return isAscending(m)155? m.headMap(Integer.MAX_VALUE, true)156: m.headMap(Integer.MIN_VALUE, true);157}158159static NavigableMap<Integer, Integer> fullTailMap(NavigableMap<Integer, Integer> m) {160return isAscending(m)161? m.tailMap(Integer.MIN_VALUE, true)162: m.tailMap(Integer.MAX_VALUE, true);163}164165static boolean isAscending(NavigableSet<Integer> s) {166var cmp = s.comparator();167return (cmp == null || cmp.compare(1, 2) < 0);168}169170static NavigableSet<Integer> fullSubSet(NavigableSet<Integer> s) {171return isAscending(s)172? s.subSet(Integer.MIN_VALUE, true, Integer.MAX_VALUE, true)173: s.subSet(Integer.MAX_VALUE, true, Integer.MIN_VALUE, true);174}175176static NavigableSet<Integer> fullHeadSet(NavigableSet<Integer> s) {177return isAscending(s)178? s.headSet(Integer.MAX_VALUE, true)179: s.headSet(Integer.MIN_VALUE, true);180}181182static NavigableSet<Integer> fullTailSet(NavigableSet<Integer> s) {183return isAscending(s)184? s.tailSet(Integer.MIN_VALUE, true)185: s.tailSet(Integer.MAX_VALUE, true);186}187188static void testEmptyCollection(Collection<?> c) {189check(c.isEmpty());190equal(c.size(), 0);191equal(c.toString(),"[]");192equal(c.toArray().length, 0);193equal(c.toArray(new Object[0]).length, 0);194195Object[] a = new Object[1]; a[0] = Boolean.TRUE;196equal(c.toArray(a), a);197equal(a[0], null);198check(! c.iterator().hasNext());199}200201static void testEmptySet(Set<?> c) {202testEmptyCollection(c);203equal(c.hashCode(), 0);204equal2(c, Collections.<Integer>emptySet());205}206207static void testEmptyMap(final Map<?,?> m) {208check(m.isEmpty());209equal(m.size(), 0);210equal(m.toString(),"{}");211equal(m.hashCode(), 0);212testEmptySet(m.keySet());213testEmptySet(m.entrySet());214testEmptyCollection(m.values());215}216217static final Random rnd;218219static {220// sufficiently random for this test221long seed = System.nanoTime();222System.out.println(LockStep.class.getCanonicalName() + ": Trial random seed: " + seed );223224rnd = new Random(seed);225}226227static void equalNext(final Iterator<?> it, Object expected) {228if (maybe(2))229check(it.hasNext());230equal(it.next(), expected);231}232233static Comparator<? super Integer> comparator(NavigableSet<Integer> s) {234var cmp = s.comparator();235return cmp != null ? cmp : Comparator.naturalOrder();236}237238static Comparator<? super Integer> comparator(NavigableMap<Integer, Integer> m) {239var cmp = m.comparator();240return cmp != null ? cmp : Comparator.naturalOrder();241}242243static void checkNavigableSet(final NavigableSet<Integer> s) {244if (s.comparator() == null)245check(s.descendingSet().descendingSet().comparator() == null);246equal(s.isEmpty(), s.size() == 0);247equal2(s, s.descendingSet());248if (maybe(4) && s instanceof Serializable) {249try {250equal2(s, serialClone(s));251} catch (RuntimeException uhoh) {252if (!(uhoh.getCause() instanceof NotSerializableException)) {253throw uhoh;254}255}256}257var cmp = comparator(s);258if (s.isEmpty()) {259THROWS(NoSuchElementException.class,260() -> s.first(),261() -> s.last());262equal(null, s.lower(1));263equal(null, s.floor(1));264equal(null, s.ceiling(1));265equal(null, s.higher(1));266} else {267Integer a = s.first();268Integer z = s.last();269equal(s.lower(a), null);270equal(s.higher(z), null);271equal2(s, s.tailSet(a));272equal2(s, s.headSet(z, true));273equal2(s, s.subSet(a, true, z, true));274275testEmptySet(s.subSet(a, true, a, false));276testEmptySet(s.subSet(z, true, z, false));277testEmptySet(s.headSet(a, false));278testEmptySet(s.tailSet(z, false));279280equal2(s.headSet(a, true), singleton(a));281equal2(s.tailSet(z, true), singleton(z));282}283Iterator<?>[] its = new Iterator[] {284s.iterator(),285s.descendingSet().descendingSet().iterator(),286};287for (final Iterator<?> it : its)288if (maybe(4))289THROWS(IllegalStateException.class, () -> it.remove());290Integer prev = null;291for (final Integer e : s) {292check(s.contains(e));293for (Iterator<?> it : its) equalNext(it, e);294equal(e, s.ceiling(e));295equal(e, s.floor(e));296check(s.higher(e) == null || cmp.compare(e, s.higher(e)) < 0);297equal(s.lower(e), prev);298if (prev != null) {299check(cmp.compare(prev, e) < 0);300}301prev = e;302}303for (final Iterator<?> it : its) {304if (maybe(2))305check(! it.hasNext());306Fun fun = () -> it.next();307THROWS(NoSuchElementException.class, fun, fun, fun);308}309}310311static void equalIterators(final Iterator<?> it1,312final Iterator<?> it2) {313while (it1.hasNext()) {314if (maybe(2))315check(it2.hasNext());316equal(it1.next(), it2.next());317}318check(! it2.hasNext());319}320321static void equalSetsLeaf(final Set<?> s1, final Set<?> s2) {322equal2(s1, s2);323equal( s1.size(), s2.size());324equal( s1.isEmpty(), s2.isEmpty());325equal( s1.hashCode(), s2.hashCode());326equal( s1.toString(), s2.toString());327equal( s1.containsAll(s2), s2.containsAll(s1));328}329330static void equalNavigableSetsLeaf(final NavigableSet<Integer> s1,331final NavigableSet<Integer> s2) {332equal2(s1, s2);333equal( s1.size(), s2.size());334equal( s1.isEmpty(), s2.isEmpty());335equal( s1.hashCode(), s2.hashCode());336equal( s1.toString(), s2.toString());337if (! s1.isEmpty()) {338equal(s1.first(), s2.first());339equal(s1.last(), s2.last());340}341equalIterators(s1.iterator(), s2.iterator());342equalIterators(s1.descendingIterator(), s2.descendingIterator());343checkNavigableSet(s1);344checkNavigableSet(s2);345}346347static void equalNavigableSets(final NavigableSet<Integer> s1,348final NavigableSet<Integer> s2) {349equalNavigableSetsLeaf(s1, s2);350equalNavigableSetsLeaf(s1.descendingSet(), s2.descendingSet());351equalNavigableSetsLeaf(s1.descendingSet().descendingSet(), s2);352Integer min = s1.isEmpty() ? Integer.MIN_VALUE : s1.first();353Integer max = s2.isEmpty() ? Integer.MAX_VALUE : s2.last();354if (s1.comparator() != null &&355s1.comparator().compare(min, max) > 0) {356Integer tmp = min; min = max; max = tmp;357}358359equalNavigableSetsLeaf(s1.subSet(min, true, max, true),360s2.subSet(min, true, max, true));361equalNavigableSetsLeaf(s1.tailSet(min, true),362s2.tailSet(min, true));363equalNavigableSetsLeaf(s1.headSet(max, true),364s2.headSet(max, true));365equalNavigableSetsLeaf((NavigableSet<Integer>) s1.subSet(min, max),366(NavigableSet<Integer>) s2.subSet(min, max));367equalNavigableSetsLeaf((NavigableSet<Integer>) s1.tailSet(min),368(NavigableSet<Integer>) s2.tailSet(min));369equalNavigableSetsLeaf((NavigableSet<Integer>) s1.headSet(max),370(NavigableSet<Integer>) s2.headSet(max));371}372373// Destined for a Collections.java near you?374static <T> T[] concat(T[]... arrays) {375int len = 0;376for (T[] arr : arrays) len += arr.length;377T[] a = (T[])java.lang.reflect.Array378.newInstance(arrays[0].getClass().getComponentType(), len);379int k = 0;380for (T[] arr : arrays) {381System.arraycopy(arr, 0, a, k, arr.length);382k += arr.length;383}384return a;385}386387static void checkNavigableMap(final NavigableMap<Integer, Integer> m) {388if (m.comparator() == null) {389check(m.descendingMap().descendingMap().comparator() == null);390check(m.descendingKeySet().descendingSet().comparator() == null);391}392equal(m.isEmpty(), m.size() == 0);393equal2(m, m.descendingMap());394if (maybe(4))395equal2(m, serialClone(m));396equal2(m.keySet(), m.descendingKeySet());397var cmp = comparator(m);398if (m.isEmpty()) {399THROWS(NoSuchElementException.class,400() -> m.firstKey(),401() -> m.lastKey());402equal(null, m.firstEntry());403equal(null, m.lastEntry());404equal(null, m.pollFirstEntry());405equal(null, m.pollLastEntry());406equal(null, m.lowerKey(1));407equal(null, m.floorKey(1));408equal(null, m.ceilingKey(1));409equal(null, m.higherKey(1));410equal(null, m.lowerEntry(1));411equal(null, m.floorEntry(1));412equal(null, m.ceilingEntry(1));413equal(null, m.higherEntry(1));414} else {415Integer a = m.firstKey();416Integer z = m.lastKey();417equal(m.lowerKey(a), null);418equal(m.higherKey(z), null);419equal(a, m.firstEntry().getKey());420equal(z, m.lastEntry().getKey());421equal2(m, m.tailMap(a));422equal2(m, m.headMap(z, true));423equal2(m, m.subMap(a, true, z, true));424425testEmptyMap(m.subMap(a, true, a, false));426testEmptyMap(m.subMap(z, true, z, false));427testEmptyMap(m.headMap(a, false));428testEmptyMap(m.tailMap(z, false));429430equal2(m.headMap(a, true), singletonMap(a, m.get(a)));431equal2(m.tailMap(z, true), singletonMap(z, m.get(z)));432}433434Iterator<?>[] kits = new Iterator[] {435m.keySet().iterator(),436m.descendingMap().descendingKeySet().iterator(),437m.descendingKeySet().descendingSet().iterator(),438};439Iterator<?>[] vits = new Iterator[] {440m.values().iterator(),441m.descendingMap().descendingMap().values().iterator(),442};443Iterator<?>[] eits = new Iterator[] {444m.entrySet().iterator(),445m.descendingMap().descendingMap().entrySet().iterator(),446};447Iterator<?>[] its = concat(kits, vits, eits);448for (final Iterator<?> it : its)449if (maybe(4))450THROWS(IllegalStateException.class, () -> it.remove());451Map.Entry<Integer, Integer> prev = null;452for (var e : m.entrySet()) {453Integer k = e.getKey();454Integer v = e.getValue();455check(m.containsKey(k));456check(m.containsValue(v));457for (var kit : kits) equalNext(kit, k);458for (var vit : vits) equalNext(vit, v);459for (var eit : eits) equalNext(eit, e);460equal(k, m.ceilingKey(k));461equal(k, m.ceilingEntry(k).getKey());462equal(k, m.floorKey(k));463equal(k, m.floorEntry(k).getKey());464check(m.higherKey(k) == null || cmp.compare(k, m.higherKey(k)) < 0);465check(m.lowerKey(k) == null || cmp.compare(k, m.lowerKey(k)) > 0);466equal(m.lowerEntry(k), prev);467if (prev == null) {468equal(m.lowerKey(k), null);469} else {470equal(m.lowerKey(k), prev.getKey());471check(cmp.compare(prev.getKey(), e.getKey()) < 0);472}473prev = e;474}475for (final var it : its) {476if (maybe(2))477check(! it.hasNext());478Fun fun = () -> it.next();479THROWS(NoSuchElementException.class, fun, fun, fun);480}481}482483static void equalNavigableMapsLeaf(final NavigableMap<Integer, Integer> m1,484final NavigableMap<Integer, Integer> m2) {485equal2(m1, m2);486equal( m1.size(), m2.size());487equal( m1.isEmpty(), m2.isEmpty());488equal( m1.hashCode(), m2.hashCode());489equal( m1.toString(), m2.toString());490equal2(m1.firstEntry(), m2.firstEntry());491equal2(m1.lastEntry(), m2.lastEntry());492checkNavigableMap(m1);493checkNavigableMap(m2);494}495496static void equalNavigableMaps(NavigableMap<Integer, Integer> m1,497NavigableMap<Integer, Integer> m2) {498equalNavigableMapsLeaf(m1, m2);499equalSetsLeaf(m1.keySet(), m2.keySet());500equalNavigableSets(m1.navigableKeySet(),501m2.navigableKeySet());502equalNavigableSets(m1.descendingKeySet(),503m2.descendingKeySet());504equal2(m1.entrySet(),505m2.entrySet());506equalNavigableMapsLeaf(m1.descendingMap(),507m2.descendingMap());508equalNavigableMapsLeaf(m1.descendingMap().descendingMap(),509m2);510equalNavigableSetsLeaf((NavigableSet<Integer>) m1.descendingMap().keySet(),511(NavigableSet<Integer>) m2.descendingMap().keySet());512equalNavigableSetsLeaf(m1.descendingMap().descendingKeySet(),513m2.descendingMap().descendingKeySet());514equal2(m1.descendingMap().entrySet(),515m2.descendingMap().entrySet());516517//----------------------------------------------------------------518// submaps519//----------------------------------------------------------------520Integer min = Integer.MIN_VALUE;521Integer max = Integer.MAX_VALUE;522if (m1.comparator() != null523&& m1.comparator().compare(min, max) > 0) {524Integer tmp = min; min = max; max = tmp;525}526switch (rnd.nextInt(6)) {527case 0:528equalNavigableMapsLeaf(m1.subMap(min, true, max, true),529m2.subMap(min, true, max, true));530break;531case 1:532equalNavigableMapsLeaf(m1.tailMap(min, true),533m2.tailMap(min, true));534break;535case 2:536equalNavigableMapsLeaf(m1.headMap(max, true),537m2.headMap(max, true));538break;539case 3:540equalNavigableMapsLeaf((NavigableMap<Integer, Integer>) m1.subMap(min, max),541(NavigableMap<Integer, Integer>) m2.subMap(min, max));542break;543case 4:544equalNavigableMapsLeaf((NavigableMap<Integer, Integer>) m1.tailMap(min),545(NavigableMap<Integer, Integer>) m2.tailMap(min));546break;547case 5:548equalNavigableMapsLeaf((NavigableMap<Integer, Integer>) m1.headMap(max),549(NavigableMap<Integer, Integer>) m2.headMap(max));550break;551}552}553554interface MapFrobber { void frob(NavigableMap<Integer, Integer> m); }555interface SetFrobber { void frob(NavigableSet<Integer> m); }556557static MapFrobber randomAdder(NavigableMap<Integer, Integer> m) {558final Integer k = unusedKey(m);559final MapFrobber[] randomAdders = {560map -> {561equal(map.put(k, k + 1), null);562equal(map.get(k), k + 1);563if (maybe(4)) {564equal(map.put(k, k + 1), k + 1);565equal(map.get(k), k + 1);}},566map -> {567map.descendingMap().put(k, k + 1);568equal(map.get(k), k + 1);},569map -> map.tailMap(k,true).headMap(k,true).put(k, k + 1),570map -> {571equal(map.tailMap(k,true).headMap(k,true).putIfAbsent(k, k + 1), null);572equal(map.tailMap(k,true).headMap(k,true).putIfAbsent(k, k + 1), k + 1);},573map -> {574equal(map.tailMap(k,true).headMap(k,true).merge(k,k,Integer::sum), k);575equal(map.tailMap(k,true).headMap(k,true).merge(k,1,Integer::sum), k+1);},576map -> equal(map.subMap(k,true, k, true).computeIfAbsent(k, key -> key + 1), k + 1),577map -> {578equal(map.subMap(k,true, k, true).computeIfPresent(k, (key, val) -> 1), null);579equal(map.tailMap(k,true).compute(k, (key, val) -> {580equal(val, null);581return 1;582}), 1);583equal(map.headMap(k, true).computeIfPresent(k, (key, val) -> val + key), k + 1);584equal(map.tailMap(k, false).computeIfPresent(k, (key, val) -> 1), null);585equal(map.headMap(k, false).compute(k, (key, val) -> null), null);586equal(map.tailMap(k, false).computeIfAbsent(k, key -> null), null);587},588map -> map.tailMap(k,true).headMap(k,true).descendingMap().put(k, k + 1)589};590return map -> {591randomAdders[rnd.nextInt(randomAdders.length)].frob(map);592if (maybe(2)) equal(map.get(k), k + 1);593if (maybe(4)) {594equal(map.put(k, k + 1), k + 1);595equal(map.get(k), k + 1);}};596}597598static SetFrobber randomAdder(NavigableSet<Integer> s) {599final Integer e = unusedElt(s);600final SetFrobber[] randomAdders = {601set -> check(set.add(e)),602set -> set.descendingSet().add(e),603set -> set.tailSet(e,true).headSet(e,true).add(e),604set -> set.descendingSet().tailSet(e,true).headSet(e,true).add(e)605};606return set -> {607if (maybe(2)) check(! set.contains(e));608randomAdders[rnd.nextInt(randomAdders.length)].frob(set);609if (maybe(2)) check(! set.add(e));610if (maybe(2)) check(set.contains(e));};611}612613static Integer unusedElt(NavigableSet<Integer> s) {614Integer e;615do { e = rnd.nextInt(1024); }616while (s.contains(e));617return e;618}619620static Integer unusedKey(NavigableMap<Integer, Integer> m) {621Integer k;622do { k = rnd.nextInt(1024); }623while (m.containsKey(k));624return k;625}626627static Integer usedKey(NavigableMap<Integer, Integer> m) {628Integer x = rnd.nextInt(1024);629Integer floor = m.floorKey(x);630Integer ceiling = m.ceilingKey(x);631if (floor != null) return floor;632check(ceiling != null);633return ceiling;634}635636static Integer usedElt(NavigableSet<Integer> s) {637Integer x = rnd.nextInt(1024);638Integer floor = s.floor(x);639Integer ceiling = s.ceiling(x);640if (floor != null) return floor;641check(ceiling != null);642return ceiling;643}644645static void checkUnusedKey(NavigableMap<Integer, Integer> m, Integer k) {646check(! m.containsKey(k));647equal(m.get(k), null);648if (maybe(2))649equal(m.remove(k), null);650}651652static void checkUnusedElt(NavigableSet<Integer> s, Integer e) {653if (maybe(2))654check(! s.contains(e));655if (maybe(2)) {656check(s.ceiling(e) != e);657check(s.floor(e) != e);658}659if (maybe(2))660check(! s.remove(e));661}662663static Fun remover(final Iterator<?> it) {664return () -> it.remove();665}666667static MapFrobber randomRemover(NavigableMap<Integer, Integer> m) {668final Integer k = usedKey(m);669final MapFrobber[] randomRemovers = {670map -> {671var e = map.firstEntry();672equal(map.pollFirstEntry(), e);673checkUnusedKey(map, e.getKey());},674map -> {675var e = map.lastEntry();676equal(map.pollLastEntry(), e);677checkUnusedKey(map, e.getKey());},678map -> {679check(map.remove(k) != null);680checkUnusedKey(map, k);},681map -> {682map.subMap(k, true, k, true).clear();683checkUnusedKey(map, k);},684map -> {685map.descendingMap().subMap(k, true, k, true).clear();686checkUnusedKey(map, k);},687map -> {688final var it = map.keySet().iterator();689while (it.hasNext())690if (it.next().equals(k)) {691it.remove();692if (maybe(2))693THROWS(IllegalStateException.class,694() -> it.remove());695}696checkUnusedKey(map, k);},697map -> {698final var it = map.navigableKeySet().descendingIterator();699while (it.hasNext())700if (it.next().equals(k)) {701it.remove();702if (maybe(2))703THROWS(IllegalStateException.class,704() -> it.remove());705}706checkUnusedKey(map, k);},707map -> {708final var it = map.entrySet().iterator();709while (it.hasNext())710if (it.next().getKey().equals(k)) {711it.remove();712if (maybe(2))713THROWS(IllegalStateException.class, remover(it));714}715checkUnusedKey(map, k);},716};717718return randomRemovers[rnd.nextInt(randomRemovers.length)];719}720721static SetFrobber randomRemover(NavigableSet<Integer> s) {722final Integer e = usedElt(s);723724final SetFrobber[] randomRemovers = {725set -> {726var fst = set.first();727equal(set.pollFirst(), fst);728checkUnusedElt(set, fst);},729set -> {730var lst = set.last();731equal(set.pollLast(), lst);732checkUnusedElt(set, lst);},733set -> {734check(set.remove(e));735checkUnusedElt(set, e);},736set -> {737set.subSet(e, true, e, true).clear();738checkUnusedElt(set, e);},739set -> {740set.descendingSet().subSet(e, true, e, true).clear();741checkUnusedElt(set, e);},742set -> {743final var it = set.iterator();744while (it.hasNext())745if (it.next().equals(e)) {746it.remove();747if (maybe(2))748THROWS(IllegalStateException.class,749() -> it.remove());750}751checkUnusedElt(set, e);},752set -> {753final var it = set.descendingSet().iterator();754while (it.hasNext())755if (it.next().equals(e)) {756it.remove();757if (maybe(2))758THROWS(IllegalStateException.class,759() -> it.remove());760}761checkUnusedElt(set, e);},762set -> {763final var it = set.descendingIterator();764while (it.hasNext())765if (it.next().equals(e)) {766it.remove();767if (maybe(2))768THROWS(IllegalStateException.class,769() -> it.remove());770}771checkUnusedElt(set, e);}772};773774return randomRemovers[rnd.nextInt(randomRemovers.length)];775}776777static void lockStep(NavigableMap<Integer, Integer> m1,778NavigableMap<Integer, Integer> m2) {779if (! (thorough || maybe(3))) return;780if (maybe(4)) m1 = serialClone(m1);781if (maybe(4)) m2 = serialClone(m2);782783var maps = Arrays.asList(m1, m2);784for (var m : maps) testEmptyMap(m);785final Set<Integer> ints = new HashSet<>();786while (ints.size() < size)787ints.add(rnd.nextInt(1024));788final Integer[] elts = ints.toArray(new Integer[size]);789equal(elts.length, size);790for (int i = 0; i < size; i++) {791MapFrobber adder = randomAdder(m1);792for (var m : maps) {793adder.frob(m);794equal(m.size(), i+1);795}796equalNavigableMaps(m1, m2);797}798for (var m : maps) {799final var e = usedKey(m);800THROWS(IllegalArgumentException.class,801() -> m.subMap(e,true,e,false).subMap(e,true,e,true),802() -> m.subMap(e,false,e,true).subMap(e,true,e,true),803() -> m.tailMap(e,false).tailMap(e,true),804() -> m.headMap(e,false).headMap(e,true),805() -> m.headMap(e, false).put(e, 0),806() -> m.tailMap(e, false).putIfAbsent(e, 0),807() -> m.headMap(e, false).computeIfAbsent(e, k -> 1),808() -> m.tailMap(e, false).compute(e, (k, v) -> 0));809}810//System.out.printf("%s%n", m1);811for (int i = size; i > 0; i--) {812MapFrobber remover = randomRemover(m1);813for (var m : maps) {814remover.frob(m);815equal(m.size(), i-1);816}817equalNavigableMaps(m1, m2);818}819for (var m : maps) testEmptyMap(m);820}821822static void lockStep(NavigableSet<Integer> s1,823NavigableSet<Integer> s2) {824if (! (thorough || maybe(3))) return;825if (maybe(4)) s1 = serialClone(s1);826if (maybe(4)) s2 = serialClone(s2);827828var sets = Arrays.asList(s1, s2);829for (var s : sets) testEmptySet(s);830final Set<Integer> ints = new HashSet<>();831while (ints.size() < size)832ints.add(rnd.nextInt(1024));833final Integer[] elts = ints.toArray(new Integer[size]);834equal(elts.length, size);835for (int i = 0; i < size; i++) {836SetFrobber adder = randomAdder(s1);837for (var s : sets) {838adder.frob(s);839equal(s.size(), i+1);840}841equalNavigableSets(s1, s2);842}843for (var s : sets) {844final Integer e = usedElt(s);845THROWS(IllegalArgumentException.class,846() -> s.subSet(e,true,e,false).subSet(e,true,e,true),847() -> s.subSet(e,false,e,true).subSet(e,true,e,true),848() -> s.tailSet(e,false).tailSet(e,true),849() -> s.headSet(e,false).headSet(e,true));850}851//System.out.printf("%s%n", s1);852for (int i = size; i > 0; i--) {853SetFrobber remover = randomRemover(s1);854for (var s : sets) {855remover.frob(s);856equal(s.size(), i-1);857}858equalNavigableSets(s1, s2);859}860for (var s : sets) testEmptySet(s);861}862863//--------------------- Infrastructure ---------------------------864static volatile int passed = 0, failed = 0;865static void pass() { passed++; }866static void fail() { failed++; Thread.dumpStack(); }867static void fail(String msg) { System.out.println(msg); fail(); }868static void unexpected(Throwable t) { failed++; t.printStackTrace(); }869static void check(boolean cond) { if (cond) pass(); else fail(); }870static void equal(Object x, Object y) {871if (Objects.equals(x, y)) pass();872else {System.out.println(x + " not equal to " + y); fail();}}873static void equal2(Object x, Object y) {equal(x, y); equal(y, x);}874public static void main(String[] args) throws Throwable {875try { realMain(args); } catch (Throwable t) { unexpected(t); }876877System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);878if (failed > 0) throw new Exception("Some tests failed");879}880interface Fun {void f() throws Throwable;}881static void THROWS(Class<? extends Throwable> k, Fun... fs) {882for (Fun f : fs)883try { f.f(); fail("Expected " + k.getName() + " not thrown"); }884catch (Throwable t) {885if (k.isAssignableFrom(t.getClass())) pass();886else unexpected(t);}}887static byte[] serializedForm(Object obj) {888try {889ByteArrayOutputStream baos = new ByteArrayOutputStream();890new ObjectOutputStream(baos).writeObject(obj);891return baos.toByteArray();892} catch (IOException e) { throw new RuntimeException(e); }}893static Object readObject(byte[] bytes)894throws IOException, ClassNotFoundException {895InputStream is = new ByteArrayInputStream(bytes);896return new ObjectInputStream(is).readObject();}897@SuppressWarnings("unchecked")898static <T> T serialClone(T obj) {899try { return (T) readObject(serializedForm(obj)); }900catch (Error|RuntimeException e) { throw e; }901catch (Throwable e) { throw new RuntimeException(e); }902}903}904905906