ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/jsr166/jsr166/src/test/jtreg/util/Spliterator/SpliteratorCollisions.java
Revision: 1.2
Committed: Tue Sep 15 05:55:05 2015 UTC (8 years, 8 months ago) by jsr166
Branch: MAIN
CVS Tags: HEAD
Changes since 1.1: +5 -5 lines
Log Message:
revert to upstream style

File Contents

# User Rev Content
1 jsr166 1.1 /*
2     * Copyright (c) 2013, 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     /**
25     * @test
26     * @bug 8005698
27     * @run testng SpliteratorCollisions
28     * @summary Spliterator traversing and splitting hash maps containing colliding hashes
29     * @author Brent Christian
30     */
31    
32     import org.testng.annotations.DataProvider;
33     import org.testng.annotations.Test;
34    
35     import java.util.ArrayDeque;
36     import java.util.ArrayList;
37     import java.util.Arrays;
38     import java.util.Collection;
39     import java.util.Collections;
40     import java.util.Deque;
41     import java.util.HashMap;
42     import java.util.HashSet;
43     import java.util.LinkedHashMap;
44     import java.util.LinkedHashSet;
45     import java.util.List;
46     import java.util.Map;
47     import java.util.Spliterator;
48     import java.util.TreeSet;
49     import java.util.function.Consumer;
50     import java.util.function.Function;
51     import java.util.function.Supplier;
52     import java.util.function.UnaryOperator;
53    
54     import static org.testng.Assert.*;
55     import static org.testng.Assert.assertEquals;
56    
57     @Test
58     public class SpliteratorCollisions {
59    
60     private static List<Integer> SIZES = Arrays.asList(0, 1, 10, 100, 1000);
61    
62     private static class SpliteratorDataBuilder<T> {
63     List<Object[]> data;
64     List<T> exp;
65 jsr166 1.2 Map<T, T> mExp;
66 jsr166 1.1
67     SpliteratorDataBuilder(List<Object[]> data, List<T> exp) {
68     this.data = data;
69     this.exp = exp;
70     this.mExp = createMap(exp);
71     }
72    
73 jsr166 1.2 Map<T, T> createMap(List<T> l) {
74     Map<T, T> m = new LinkedHashMap<>();
75 jsr166 1.1 for (T t : l) {
76     m.put(t, t);
77     }
78     return m;
79     }
80    
81     void add(String description, Collection<?> expected, Supplier<Spliterator<?>> s) {
82     description = joiner(description).toString();
83     data.add(new Object[]{description, expected, s});
84     }
85    
86     void add(String description, Supplier<Spliterator<?>> s) {
87     add(description, exp, s);
88     }
89    
90     void addCollection(Function<Collection<T>, ? extends Collection<T>> c) {
91     add("new " + c.apply(Collections.<T>emptyList()).getClass().getName() + ".spliterator()",
92     () -> c.apply(exp).spliterator());
93     }
94    
95     void addList(Function<Collection<T>, ? extends List<T>> l) {
96     // @@@ If collection is instance of List then add sub-list tests
97     addCollection(l);
98     }
99    
100 jsr166 1.2 void addMap(Function<Map<T, T>, ? extends Map<T, T>> m) {
101     String description = "new " + m.apply(Collections.<T, T>emptyMap()).getClass().getName();
102 jsr166 1.1 add(description + ".keySet().spliterator()", () -> m.apply(mExp).keySet().spliterator());
103     add(description + ".values().spliterator()", () -> m.apply(mExp).values().spliterator());
104     add(description + ".entrySet().spliterator()", mExp.entrySet(), () -> m.apply(mExp).entrySet().spliterator());
105     }
106    
107     StringBuilder joiner(String description) {
108     return new StringBuilder(description).
109     append(" {").
110     append("size=").append(exp.size()).
111     append("}");
112     }
113     }
114    
115     static Object[][] spliteratorDataProvider;
116    
117     @DataProvider(name = "HashableIntSpliterator")
118     public static Object[][] spliteratorDataProvider() {
119     if (spliteratorDataProvider != null) {
120     return spliteratorDataProvider;
121     }
122    
123     List<Object[]> data = new ArrayList<>();
124     for (int size : SIZES) {
125     List<HashableInteger> exp = listIntRange(size, false);
126     SpliteratorDataBuilder<HashableInteger> db = new SpliteratorDataBuilder<>(data, exp);
127    
128     // Maps
129     db.addMap(HashMap::new);
130     db.addMap(LinkedHashMap::new);
131    
132     // Collections that use HashMap
133     db.addCollection(HashSet::new);
134     db.addCollection(LinkedHashSet::new);
135     db.addCollection(TreeSet::new);
136     }
137     return spliteratorDataProvider = data.toArray(new Object[0][]);
138     }
139    
140     static Object[][] spliteratorDataProviderWithNull;
141    
142     @DataProvider(name = "HashableIntSpliteratorWithNull")
143     public static Object[][] spliteratorNullDataProvider() {
144     if (spliteratorDataProviderWithNull != null) {
145     return spliteratorDataProviderWithNull;
146     }
147    
148     List<Object[]> data = new ArrayList<>();
149     for (int size : SIZES) {
150     List<HashableInteger> exp = listIntRange(size, true);
151     SpliteratorDataBuilder<HashableInteger> db = new SpliteratorDataBuilder<>(data, exp);
152    
153     // Maps
154     db.addMap(HashMap::new);
155     db.addMap(LinkedHashMap::new);
156     // TODO: add this back in if we decide to keep TreeBin in WeakHashMap
157     //db.addMap(WeakHashMap::new);
158    
159     // Collections that use HashMap
160     db.addCollection(HashSet::new);
161     db.addCollection(LinkedHashSet::new);
162     // db.addCollection(TreeSet::new);
163    
164     }
165     return spliteratorDataProviderWithNull = data.toArray(new Object[0][]);
166     }
167    
168     static final class HashableInteger implements Comparable<HashableInteger> {
169    
170     final int value;
171     final int hashmask; //yes duplication
172    
173     HashableInteger(int value, int hashmask) {
174     this.value = value;
175     this.hashmask = hashmask;
176     }
177    
178     @Override
179     public boolean equals(Object obj) {
180     if (obj instanceof HashableInteger) {
181     HashableInteger other = (HashableInteger) obj;
182    
183     return other.value == value;
184     }
185    
186     return false;
187     }
188    
189     @Override
190     public int hashCode() {
191     return value % hashmask;
192     }
193    
194     @Override
195     public int compareTo(HashableInteger o) {
196     return value - o.value;
197     }
198    
199     @Override
200     public String toString() {
201     return Integer.toString(value);
202     }
203     }
204    
205     private static List<HashableInteger> listIntRange(int upTo, boolean withNull) {
206     List<HashableInteger> exp = new ArrayList<>();
207     if (withNull) {
208     exp.add(null);
209     }
210     for (int i = 0; i < upTo; i++) {
211     exp.add(new HashableInteger(i, 10));
212     }
213     return Collections.unmodifiableList(exp);
214     }
215    
216     @Test(dataProvider = "HashableIntSpliterator")
217     @SuppressWarnings({"unchecked", "rawtypes"})
218     public void testNullPointerException(String description, Collection exp, Supplier<Spliterator> s) {
219     executeAndCatch(NullPointerException.class, () -> s.get().forEachRemaining(null));
220     executeAndCatch(NullPointerException.class, () -> s.get().tryAdvance(null));
221     }
222    
223     @Test(dataProvider = "HashableIntSpliteratorWithNull")
224     @SuppressWarnings({"unchecked", "rawtypes"})
225     public void testNullPointerExceptionWithNull(String description, Collection exp, Supplier<Spliterator> s) {
226     executeAndCatch(NullPointerException.class, () -> s.get().forEachRemaining(null));
227     executeAndCatch(NullPointerException.class, () -> s.get().tryAdvance(null));
228     }
229    
230    
231     @Test(dataProvider = "HashableIntSpliterator")
232     @SuppressWarnings({"unchecked", "rawtypes"})
233     public void testForEach(String description, Collection exp, Supplier<Spliterator> s) {
234     testForEach(exp, s, (Consumer<Object> b) -> b);
235     }
236    
237     @Test(dataProvider = "HashableIntSpliteratorWithNull")
238     @SuppressWarnings({"unchecked", "rawtypes"})
239     public void testForEachWithNull(String description, Collection exp, Supplier<Spliterator> s) {
240     testForEach(exp, s, (Consumer<Object> b) -> b);
241     }
242    
243    
244     @Test(dataProvider = "HashableIntSpliterator")
245     @SuppressWarnings({"unchecked", "rawtypes"})
246     public void testTryAdvance(String description, Collection exp, Supplier<Spliterator> s) {
247     testTryAdvance(exp, s, (Consumer<Object> b) -> b);
248     }
249    
250     @Test(dataProvider = "HashableIntSpliteratorWithNull")
251     @SuppressWarnings({"unchecked", "rawtypes"})
252     public void testTryAdvanceWithNull(String description, Collection exp, Supplier<Spliterator> s) {
253     testTryAdvance(exp, s, (Consumer<Object> b) -> b);
254     }
255    
256     /* skip this test until 8013649 is fixed
257     @Test(dataProvider = "HashableIntSpliterator")
258     @SuppressWarnings({"unchecked", "rawtypes"})
259     public void testMixedTryAdvanceForEach(String description, Collection exp, Supplier<Spliterator> s) {
260     testMixedTryAdvanceForEach(exp, s, (Consumer<Object> b) -> b);
261     }
262    
263     @Test(dataProvider = "HashableIntSpliteratorWithNull")
264     @SuppressWarnings({"unchecked", "rawtypes"})
265     public void testMixedTryAdvanceForEachWithNull(String description, Collection exp, Supplier<Spliterator> s) {
266     testMixedTryAdvanceForEach(exp, s, (Consumer<Object> b) -> b);
267     }
268     */
269    
270     @Test(dataProvider = "HashableIntSpliterator")
271     @SuppressWarnings({"unchecked", "rawtypes"})
272     public void testSplitAfterFullTraversal(String description, Collection exp, Supplier<Spliterator> s) {
273     testSplitAfterFullTraversal(s, (Consumer<Object> b) -> b);
274     }
275    
276     @Test(dataProvider = "HashableIntSpliteratorWithNull")
277     @SuppressWarnings({"unchecked", "rawtypes"})
278     public void testSplitAfterFullTraversalWithNull(String description, Collection exp, Supplier<Spliterator> s) {
279     testSplitAfterFullTraversal(s, (Consumer<Object> b) -> b);
280     }
281    
282    
283     @Test(dataProvider = "HashableIntSpliterator")
284     @SuppressWarnings({"unchecked", "rawtypes"})
285     public void testSplitOnce(String description, Collection exp, Supplier<Spliterator> s) {
286     testSplitOnce(exp, s, (Consumer<Object> b) -> b);
287     }
288    
289     @Test(dataProvider = "HashableIntSpliteratorWithNull")
290     @SuppressWarnings({"unchecked", "rawtypes"})
291     public void testSplitOnceWithNull(String description, Collection exp, Supplier<Spliterator> s) {
292     testSplitOnce(exp, s, (Consumer<Object> b) -> b);
293     }
294    
295     @Test(dataProvider = "HashableIntSpliterator")
296     @SuppressWarnings({"unchecked", "rawtypes"})
297     public void testSplitSixDeep(String description, Collection exp, Supplier<Spliterator> s) {
298     testSplitSixDeep(exp, s, (Consumer<Object> b) -> b);
299     }
300    
301     @Test(dataProvider = "HashableIntSpliteratorWithNull")
302     @SuppressWarnings({"unchecked", "rawtypes"})
303     public void testSplitSixDeepWithNull(String description, Collection exp, Supplier<Spliterator> s) {
304     testSplitSixDeep(exp, s, (Consumer<Object> b) -> b);
305     }
306    
307     @Test(dataProvider = "HashableIntSpliterator")
308     @SuppressWarnings({"unchecked", "rawtypes"})
309     public void testSplitUntilNull(String description, Collection exp, Supplier<Spliterator> s) {
310     testSplitUntilNull(exp, s, (Consumer<Object> b) -> b);
311     }
312    
313     @Test(dataProvider = "HashableIntSpliteratorWithNull")
314     @SuppressWarnings({"unchecked", "rawtypes"})
315     public void testSplitUntilNullWithNull(String description, Collection exp, Supplier<Spliterator> s) {
316     testSplitUntilNull(exp, s, (Consumer<Object> b) -> b);
317     }
318    
319     private static <T, S extends Spliterator<T>> void testForEach(
320     Collection<T> exp,
321     Supplier<S> supplier,
322     UnaryOperator<Consumer<T>> boxingAdapter) {
323     S spliterator = supplier.get();
324     long sizeIfKnown = spliterator.getExactSizeIfKnown();
325     boolean isOrdered = spliterator.hasCharacteristics(Spliterator.ORDERED);
326    
327     ArrayList<T> fromForEach = new ArrayList<>();
328     spliterator = supplier.get();
329     Consumer<T> addToFromForEach = boxingAdapter.apply(fromForEach::add);
330     spliterator.forEachRemaining(addToFromForEach);
331    
332     // Assert that forEach now produces no elements
333     spliterator.forEachRemaining(boxingAdapter.apply(e -> fail("Spliterator.forEach produced an element after spliterator exhausted: " + e)));
334     // Assert that tryAdvance now produce no elements
335     spliterator.tryAdvance(boxingAdapter.apply(e -> fail("Spliterator.tryAdvance produced an element after spliterator exhausted: " + e)));
336    
337     // assert that size, tryAdvance, and forEach are consistent
338     if (sizeIfKnown >= 0) {
339     assertEquals(sizeIfKnown, exp.size());
340     }
341     if (exp.contains(null)) {
342     assertTrue(fromForEach.contains(null));
343     }
344     assertEquals(fromForEach.size(), exp.size());
345    
346     assertContents(fromForEach, exp, isOrdered);
347     }
348    
349     private static <T, S extends Spliterator<T>> void testTryAdvance(
350     Collection<T> exp,
351     Supplier<S> supplier,
352     UnaryOperator<Consumer<T>> boxingAdapter) {
353     S spliterator = supplier.get();
354     long sizeIfKnown = spliterator.getExactSizeIfKnown();
355     boolean isOrdered = spliterator.hasCharacteristics(Spliterator.ORDERED);
356    
357     spliterator = supplier.get();
358     ArrayList<T> fromTryAdvance = new ArrayList<>();
359     Consumer<T> addToFromTryAdvance = boxingAdapter.apply(fromTryAdvance::add);
360     while (spliterator.tryAdvance(addToFromTryAdvance)) { }
361    
362     // Assert that forEach now produces no elements
363     spliterator.forEachRemaining(boxingAdapter.apply(e -> fail("Spliterator.forEach produced an element after spliterator exhausted: " + e)));
364     // Assert that tryAdvance now produce no elements
365     spliterator.tryAdvance(boxingAdapter.apply(e -> fail("Spliterator.tryAdvance produced an element after spliterator exhausted: " + e)));
366    
367     // assert that size, tryAdvance, and forEach are consistent
368     if (sizeIfKnown >= 0) {
369     assertEquals(sizeIfKnown, exp.size());
370     }
371     assertEquals(fromTryAdvance.size(), exp.size());
372    
373     assertContents(fromTryAdvance, exp, isOrdered);
374     }
375    
376     private static <T, S extends Spliterator<T>> void testMixedTryAdvanceForEach(
377     Collection<T> exp,
378     Supplier<S> supplier,
379     UnaryOperator<Consumer<T>> boxingAdapter) {
380     S spliterator = supplier.get();
381     long sizeIfKnown = spliterator.getExactSizeIfKnown();
382     boolean isOrdered = spliterator.hasCharacteristics(Spliterator.ORDERED);
383    
384     // tryAdvance first few elements, then forEach rest
385     ArrayList<T> dest = new ArrayList<>();
386     spliterator = supplier.get();
387     Consumer<T> addToDest = boxingAdapter.apply(dest::add);
388     for (int i = 0; i < 10 && spliterator.tryAdvance(addToDest); i++) { }
389     spliterator.forEachRemaining(addToDest);
390    
391     // Assert that forEach now produces no elements
392     spliterator.forEachRemaining(boxingAdapter.apply(e -> fail("Spliterator.forEach produced an element after spliterator exhausted: " + e)));
393     // Assert that tryAdvance now produce no elements
394     spliterator.tryAdvance(boxingAdapter.apply(e -> fail("Spliterator.tryAdvance produced an element after spliterator exhausted: " + e)));
395    
396     if (sizeIfKnown >= 0) {
397     assertEquals(sizeIfKnown, dest.size());
398     }
399     assertEquals(dest.size(), exp.size());
400    
401     if (isOrdered) {
402     assertEquals(dest, exp);
403     }
404     else {
405     assertContentsUnordered(dest, exp);
406     }
407     }
408    
409     private static <T, S extends Spliterator<T>> void testSplitAfterFullTraversal(
410     Supplier<S> supplier,
411     UnaryOperator<Consumer<T>> boxingAdapter) {
412     // Full traversal using tryAdvance
413     Spliterator<T> spliterator = supplier.get();
414     while (spliterator.tryAdvance(boxingAdapter.apply(e -> { }))) { }
415     Spliterator<T> split = spliterator.trySplit();
416     assertNull(split);
417    
418     // Full traversal using forEach
419     spliterator = supplier.get();
420     spliterator.forEachRemaining(boxingAdapter.apply(e -> {
421     }));
422     split = spliterator.trySplit();
423     assertNull(split);
424    
425     // Full traversal using tryAdvance then forEach
426     spliterator = supplier.get();
427     spliterator.tryAdvance(boxingAdapter.apply(e -> { }));
428     spliterator.forEachRemaining(boxingAdapter.apply(e -> {
429     }));
430     split = spliterator.trySplit();
431     assertNull(split);
432     }
433    
434     private static <T, S extends Spliterator<T>> void testSplitOnce(
435     Collection<T> exp,
436     Supplier<S> supplier,
437     UnaryOperator<Consumer<T>> boxingAdapter) {
438     S spliterator = supplier.get();
439     long sizeIfKnown = spliterator.getExactSizeIfKnown();
440     boolean isOrdered = spliterator.hasCharacteristics(Spliterator.ORDERED);
441    
442     ArrayList<T> fromSplit = new ArrayList<>();
443     Spliterator<T> s1 = supplier.get();
444     Spliterator<T> s2 = s1.trySplit();
445     long s1Size = s1.getExactSizeIfKnown();
446     long s2Size = (s2 != null) ? s2.getExactSizeIfKnown() : 0;
447    
448     Consumer<T> addToFromSplit = boxingAdapter.apply(fromSplit::add);
449     if (s2 != null)
450     s2.forEachRemaining(addToFromSplit);
451     s1.forEachRemaining(addToFromSplit);
452    
453     if (sizeIfKnown >= 0) {
454     assertEquals(sizeIfKnown, fromSplit.size());
455     if (s1Size >= 0 && s2Size >= 0)
456     assertEquals(sizeIfKnown, s1Size + s2Size);
457     }
458     assertContents(fromSplit, exp, isOrdered);
459     }
460    
461     private static <T, S extends Spliterator<T>> void testSplitSixDeep(
462     Collection<T> exp,
463     Supplier<S> supplier,
464     UnaryOperator<Consumer<T>> boxingAdapter) {
465     S spliterator = supplier.get();
466     boolean isOrdered = spliterator.hasCharacteristics(Spliterator.ORDERED);
467    
468     for (int depth=0; depth < 6; depth++) {
469     List<T> dest = new ArrayList<>();
470     spliterator = supplier.get();
471    
472     assertSpliterator(spliterator);
473    
474     // verify splitting with forEach
475     visit(depth, 0, dest, spliterator, boxingAdapter, spliterator.characteristics(), false);
476     assertContents(dest, exp, isOrdered);
477    
478     // verify splitting with tryAdvance
479     dest.clear();
480     spliterator = supplier.get();
481     visit(depth, 0, dest, spliterator, boxingAdapter, spliterator.characteristics(), true);
482     assertContents(dest, exp, isOrdered);
483     }
484     }
485    
486     private static <T, S extends Spliterator<T>> void visit(int depth, int curLevel,
487     List<T> dest, S spliterator, UnaryOperator<Consumer<T>> boxingAdapter,
488     int rootCharacteristics, boolean useTryAdvance) {
489     if (curLevel < depth) {
490     long beforeSize = spliterator.getExactSizeIfKnown();
491     Spliterator<T> split = spliterator.trySplit();
492     if (split != null) {
493     assertSpliterator(split, rootCharacteristics);
494     assertSpliterator(spliterator, rootCharacteristics);
495    
496     if ((rootCharacteristics & Spliterator.SUBSIZED) != 0 &&
497     (rootCharacteristics & Spliterator.SIZED) != 0) {
498     assertEquals(beforeSize, split.estimateSize() + spliterator.estimateSize());
499     }
500     visit(depth, curLevel + 1, dest, split, boxingAdapter, rootCharacteristics, useTryAdvance);
501     }
502     visit(depth, curLevel + 1, dest, spliterator, boxingAdapter, rootCharacteristics, useTryAdvance);
503     }
504     else {
505     long sizeIfKnown = spliterator.getExactSizeIfKnown();
506     if (useTryAdvance) {
507     Consumer<T> addToDest = boxingAdapter.apply(dest::add);
508     int count = 0;
509     while (spliterator.tryAdvance(addToDest)) {
510     ++count;
511     }
512    
513     if (sizeIfKnown >= 0)
514     assertEquals(sizeIfKnown, count);
515    
516     // Assert that forEach now produces no elements
517     spliterator.forEachRemaining(boxingAdapter.apply(e -> fail("Spliterator.forEach produced an element after spliterator exhausted: " + e)));
518    
519     Spliterator<T> split = spliterator.trySplit();
520     assertNull(split);
521     }
522     else {
523     List<T> leafDest = new ArrayList<>();
524     Consumer<T> addToLeafDest = boxingAdapter.apply(leafDest::add);
525     spliterator.forEachRemaining(addToLeafDest);
526    
527     if (sizeIfKnown >= 0)
528     assertEquals(sizeIfKnown, leafDest.size());
529    
530     // Assert that forEach now produces no elements
531     spliterator.tryAdvance(boxingAdapter.apply(e -> fail("Spliterator.tryAdvance produced an element after spliterator exhausted: " + e)));
532    
533     Spliterator<T> split = spliterator.trySplit();
534     assertNull(split);
535    
536     dest.addAll(leafDest);
537     }
538     }
539     }
540    
541     private static <T, S extends Spliterator<T>> void testSplitUntilNull(
542     Collection<T> exp,
543     Supplier<S> supplier,
544     UnaryOperator<Consumer<T>> boxingAdapter) {
545     Spliterator<T> s = supplier.get();
546     boolean isOrdered = s.hasCharacteristics(Spliterator.ORDERED);
547     assertSpliterator(s);
548    
549     List<T> splits = new ArrayList<>();
550     Consumer<T> c = boxingAdapter.apply(splits::add);
551    
552     testSplitUntilNull(new SplitNode<T>(c, s));
553     assertContents(splits, exp, isOrdered);
554     }
555    
556     private static class SplitNode<T> {
557     // Constant for every node
558     final Consumer<T> c;
559     final int rootCharacteristics;
560    
561     final Spliterator<T> s;
562    
563     SplitNode(Consumer<T> c, Spliterator<T> s) {
564     this(c, s.characteristics(), s);
565     }
566    
567     private SplitNode(Consumer<T> c, int rootCharacteristics, Spliterator<T> s) {
568     this.c = c;
569     this.rootCharacteristics = rootCharacteristics;
570     this.s = s;
571     }
572    
573     SplitNode<T> fromSplit(Spliterator<T> split) {
574     return new SplitNode<>(c, rootCharacteristics, split);
575     }
576     }
577    
578     /**
579     * Set the maximum stack capacity to 0.25MB. This should be more than enough to detect a bad spliterator
580     * while not unduly disrupting test infrastructure given the test data sizes that are used are small.
581     * Note that j.u.c.ForkJoinPool sets the max queue size to 64M (1 << 26).
582     */
583     private static final int MAXIMUM_STACK_CAPACITY = 1 << 18; // 0.25MB
584    
585     private static <T> void testSplitUntilNull(SplitNode<T> e) {
586     // Use an explicit stack to avoid a StackOverflowException when testing a Spliterator
587     // that when repeatedly split produces a right-balanced (and maybe degenerate) tree, or
588     // for a spliterator that is badly behaved.
589     Deque<SplitNode<T>> stack = new ArrayDeque<>();
590     stack.push(e);
591    
592     int iteration = 0;
593     while (!stack.isEmpty()) {
594     assertTrue(iteration++ < MAXIMUM_STACK_CAPACITY, "Exceeded maximum stack modification count of 1 << 18");
595    
596     e = stack.pop();
597     Spliterator<T> parentAndRightSplit = e.s;
598    
599     long parentEstimateSize = parentAndRightSplit.estimateSize();
600     assertTrue(parentEstimateSize >= 0,
601     String.format("Split size estimate %d < 0", parentEstimateSize));
602    
603     long parentSize = parentAndRightSplit.getExactSizeIfKnown();
604     Spliterator<T> leftSplit = parentAndRightSplit.trySplit();
605     if (leftSplit == null) {
606     parentAndRightSplit.forEachRemaining(e.c);
607     continue;
608     }
609    
610     assertSpliterator(leftSplit, e.rootCharacteristics);
611     assertSpliterator(parentAndRightSplit, e.rootCharacteristics);
612    
613     if (parentEstimateSize != Long.MAX_VALUE && leftSplit.estimateSize() > 0 && parentAndRightSplit.estimateSize() > 0) {
614     assertTrue(leftSplit.estimateSize() < parentEstimateSize,
615     String.format("Left split size estimate %d >= parent split size estimate %d", leftSplit.estimateSize(), parentEstimateSize));
616     assertTrue(parentAndRightSplit.estimateSize() < parentEstimateSize,
617     String.format("Right split size estimate %d >= parent split size estimate %d", leftSplit.estimateSize(), parentEstimateSize));
618     }
619     else {
620     assertTrue(leftSplit.estimateSize() <= parentEstimateSize,
621     String.format("Left split size estimate %d > parent split size estimate %d", leftSplit.estimateSize(), parentEstimateSize));
622     assertTrue(parentAndRightSplit.estimateSize() <= parentEstimateSize,
623     String.format("Right split size estimate %d > parent split size estimate %d", leftSplit.estimateSize(), parentEstimateSize));
624     }
625    
626     long leftSize = leftSplit.getExactSizeIfKnown();
627     long rightSize = parentAndRightSplit.getExactSizeIfKnown();
628     if (parentSize >= 0 && leftSize >= 0 && rightSize >= 0)
629     assertEquals(parentSize, leftSize + rightSize,
630     String.format("exact left split size %d + exact right split size %d != parent exact split size %d",
631     leftSize, rightSize, parentSize));
632    
633     // Add right side to stack first so left side is popped off first
634     stack.push(e.fromSplit(parentAndRightSplit));
635     stack.push(e.fromSplit(leftSplit));
636     }
637     }
638    
639     private static void assertSpliterator(Spliterator<?> s, int rootCharacteristics) {
640     if ((rootCharacteristics & Spliterator.SUBSIZED) != 0) {
641     assertTrue(s.hasCharacteristics(Spliterator.SUBSIZED),
642     "Child split is not SUBSIZED when root split is SUBSIZED");
643     }
644     assertSpliterator(s);
645     }
646    
647     private static void assertSpliterator(Spliterator<?> s) {
648     if (s.hasCharacteristics(Spliterator.SUBSIZED)) {
649     assertTrue(s.hasCharacteristics(Spliterator.SIZED));
650     }
651     if (s.hasCharacteristics(Spliterator.SIZED)) {
652     assertTrue(s.estimateSize() != Long.MAX_VALUE);
653     assertTrue(s.getExactSizeIfKnown() >= 0);
654     }
655     try {
656     s.getComparator();
657     assertTrue(s.hasCharacteristics(Spliterator.SORTED));
658     } catch (IllegalStateException e) {
659     assertFalse(s.hasCharacteristics(Spliterator.SORTED));
660     }
661     }
662    
663     private static<T> void assertContents(Collection<T> actual, Collection<T> expected, boolean isOrdered) {
664     if (isOrdered) {
665     assertEquals(actual, expected);
666     }
667     else {
668     assertContentsUnordered(actual, expected);
669     }
670     }
671    
672     private static<T> void assertContentsUnordered(Iterable<T> actual, Iterable<T> expected) {
673     assertEquals(toBoxedMultiset(actual), toBoxedMultiset(expected));
674     }
675    
676     private static <T> Map<T, HashableInteger> toBoxedMultiset(Iterable<T> c) {
677     Map<T, HashableInteger> result = new HashMap<>();
678     c.forEach(e -> {
679     if (result.containsKey(e)) {
680     result.put(e, new HashableInteger(result.get(e).value + 1, 10));
681     } else {
682     result.put(e, new HashableInteger(1, 10));
683     }
684     });
685     return result;
686     }
687    
688     private void executeAndCatch(Class<? extends Exception> expected, Runnable r) {
689     Exception caught = null;
690     try {
691     r.run();
692     }
693     catch (Exception e) {
694     caught = e;
695     }
696    
697     assertNotNull(caught,
698     String.format("No Exception was thrown, expected an Exception of %s to be thrown",
699     expected.getName()));
700     assertTrue(expected.isInstance(caught),
701     String.format("Exception thrown %s not an instance of %s",
702     caught.getClass().getName(), expected.getName()));
703     }
704    
705     }