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  * @summary Spliterator traversing and splitting tests
  27  * @run testng SpliteratorTraversingAndSplittingTest
  28  */
  29 
  30 import org.testng.annotations.DataProvider;
  31 import org.testng.annotations.Test;
  32 
  33 import java.util.AbstractCollection;
  34 import java.util.AbstractList;
  35 import java.util.AbstractSet;
  36 import java.util.ArrayDeque;
  37 import java.util.ArrayList;
  38 import java.util.Arrays;
  39 import java.util.Collection;
  40 import java.util.Collections;
  41 import java.util.Comparator;
  42 import java.util.Deque;
  43 import java.util.HashMap;
  44 import java.util.HashSet;
  45 import java.util.IdentityHashMap;
  46 import java.util.Iterator;
  47 import java.util.LinkedHashMap;
  48 import java.util.LinkedHashSet;
  49 import java.util.LinkedList;
  50 import java.util.List;
  51 import java.util.Map;
  52 import java.util.PriorityQueue;
  53 import java.util.Set;
  54 import java.util.SortedSet;
  55 import java.util.Spliterator;
  56 import java.util.Spliterators;
  57 import java.util.Stack;
  58 import java.util.TreeMap;
  59 import java.util.TreeSet;
  60 import java.util.Vector;
  61 import java.util.WeakHashMap;
  62 import java.util.concurrent.ArrayBlockingQueue;
  63 import java.util.concurrent.ConcurrentHashMap;
  64 import java.util.concurrent.ConcurrentLinkedQueue;
  65 import java.util.concurrent.ConcurrentSkipListMap;
  66 import java.util.concurrent.ConcurrentSkipListSet;
  67 import java.util.concurrent.CopyOnWriteArrayList;
  68 import java.util.concurrent.CopyOnWriteArraySet;
  69 import java.util.concurrent.LinkedBlockingDeque;
  70 import java.util.concurrent.LinkedBlockingQueue;
  71 import java.util.concurrent.LinkedTransferQueue;
  72 import java.util.concurrent.PriorityBlockingQueue;
  73 import java.util.function.Consumer;
  74 import java.util.function.DoubleConsumer;
  75 import java.util.function.Function;
  76 import java.util.function.IntConsumer;
  77 import java.util.function.LongConsumer;
  78 import java.util.function.Supplier;
  79 import java.util.function.UnaryOperator;
  80 
  81 import static org.testng.Assert.*;
  82 import static org.testng.Assert.assertEquals;
  83 
  84 @Test
  85 public class SpliteratorTraversingAndSplittingTest {
  86 
  87     private static List<Integer> SIZES = Arrays.asList(0, 1, 10, 100, 1000);
  88 
  89     private static class SpliteratorDataBuilder<T> {
  90         List<Object[]> data;
  91 
  92         List<T> exp;
  93 
  94         Map<T, T> mExp;
  95 
  96         SpliteratorDataBuilder(List<Object[]> data, List<T> exp) {
  97             this.data = data;
  98             this.exp = exp;
  99             this.mExp = createMap(exp);
 100         }
 101 
 102         Map<T, T> createMap(List<T> l) {
 103             Map<T, T> m = new LinkedHashMap<>();
 104             for (T t : l) {
 105                 m.put(t, t);
 106             }
 107             return m;
 108         }
 109 
 110         void add(String description, Collection<?> expected, Supplier<Spliterator<?>> s) {
 111             description = joiner(description).toString();
 112             data.add(new Object[]{description, expected, s});
 113         }
 114 
 115         void add(String description, Supplier<Spliterator<?>> s) {
 116             add(description, exp, s);
 117         }
 118 
 119         void addCollection(Function<Collection<T>, ? extends Collection<T>> c) {
 120             add("new " + c.apply(Collections.<T>emptyList()).getClass().getName() + ".spliterator()",
 121                 () -> c.apply(exp).spliterator());
 122         }
 123 
 124         void addList(Function<Collection<T>, ? extends List<T>> l) {
 125             // @@@ If collection is instance of List then add sub-list tests
 126             addCollection(l);
 127         }
 128 
 129         void addMap(Function<Map<T, T>, ? extends Map<T, T>> m) {
 130             String description = "new " + m.apply(Collections.<T, T>emptyMap()).getClass().getName();
 131             addMap(m, description);
 132         }
 133 
 134         void addMap(Function<Map<T, T>, ? extends Map<T, T>> m, String description) {
 135             add(description + ".keySet().spliterator()", () -> m.apply(mExp).keySet().spliterator());
 136             add(description + ".values().spliterator()", () -> m.apply(mExp).values().spliterator());
 137             add(description + ".entrySet().spliterator()", mExp.entrySet(), () -> m.apply(mExp).entrySet().spliterator());
 138         }
 139 
 140         StringBuilder joiner(String description) {
 141             return new StringBuilder(description).
 142                     append(" {").
 143                     append("size=").append(exp.size()).
 144                     append("}");
 145         }
 146     }
 147 
 148     static Object[][] spliteratorDataProvider;
 149 
 150     @DataProvider(name = "Spliterator<Integer>")
 151     public static Object[][] spliteratorDataProvider() {
 152         if (spliteratorDataProvider != null) {
 153             return spliteratorDataProvider;
 154         }
 155 
 156         List<Object[]> data = new ArrayList<>();
 157         for (int size : SIZES) {
 158             List<Integer> exp = listIntRange(size);
 159             SpliteratorDataBuilder<Integer> db = new SpliteratorDataBuilder<>(data, exp);
 160 
 161             // Direct spliterator methods
 162 
 163             db.add("Spliterators.spliterator(Collection, ...)",
 164                    () -> Spliterators.spliterator(exp, 0));
 165 
 166             db.add("Spliterators.spliterator(Iterator, ...)",
 167                    () -> Spliterators.spliterator(exp.iterator(), exp.size(), 0));
 168 
 169             db.add("Spliterators.spliteratorUnknownSize(Iterator, ...)",
 170                    () -> Spliterators.spliteratorUnknownSize(exp.iterator(), 0));
 171 
 172             db.add("Spliterators.spliterator(Spliterators.iteratorFromSpliterator(Spliterator ), ...)",
 173                    () -> Spliterators.spliterator(Spliterators.iterator(exp.spliterator()), exp.size(), 0));
 174 
 175             db.add("Spliterators.spliterator(T[], ...)",
 176                    () -> Spliterators.spliterator(exp.toArray(new Integer[0]), 0));
 177 
 178             db.add("Arrays.spliterator(T[], ...)",
 179                    () -> Arrays.spliterator(exp.toArray(new Integer[0])));
 180 
 181             class SpliteratorFromIterator extends Spliterators.AbstractSpliterator<Integer> {
 182                 Iterator<Integer> it;
 183 
 184                 SpliteratorFromIterator(Iterator<Integer> it, long est) {
 185                     super(est, Spliterator.SIZED);
 186                     this.it = it;
 187                 }
 188 
 189                 @Override
 190                 public boolean tryAdvance(Consumer<? super Integer> action) {
 191                     if (action == null)
 192                         throw new NullPointerException();
 193                     if (it.hasNext()) {
 194                         action.accept(it.next());
 195                         return true;
 196                     }
 197                     else {
 198                         return false;
 199                     }
 200                 }
 201             }
 202             db.add("new Spliterators.AbstractSpliterator()",
 203                    () -> new SpliteratorFromIterator(exp.iterator(), exp.size()));
 204 
 205             // Collections
 206 
 207             // default method implementations
 208 
 209             class AbstractCollectionImpl extends AbstractCollection<Integer> {
 210                 Collection<Integer> c;
 211 
 212                 AbstractCollectionImpl(Collection<Integer> c) {
 213                     this.c = c;
 214                 }
 215 
 216                 @Override
 217                 public Iterator<Integer> iterator() {
 218                     return c.iterator();
 219                 }
 220 
 221                 @Override
 222                 public int size() {
 223                     return c.size();
 224                 }
 225             }
 226             db.addCollection(
 227                     c -> new AbstractCollectionImpl(c));
 228 
 229             class AbstractListImpl extends AbstractList<Integer> {
 230                 List<Integer> l;
 231 
 232                 AbstractListImpl(Collection<Integer> c) {
 233                     this.l = new ArrayList<>(c);
 234                 }
 235 
 236                 @Override
 237                 public Integer get(int index) {
 238                     return l.get(index);
 239                 }
 240 
 241                 @Override
 242                 public int size() {
 243                     return l.size();
 244                 }
 245             }
 246             db.addCollection(
 247                     c -> new AbstractListImpl(c));
 248 
 249             class AbstractSetImpl extends AbstractSet<Integer> {
 250                 Set<Integer> s;
 251 
 252                 AbstractSetImpl(Collection<Integer> c) {
 253                     this.s = new HashSet<>(c);
 254                 }
 255 
 256                 @Override
 257                 public Iterator<Integer> iterator() {
 258                     return s.iterator();
 259                 }
 260 
 261                 @Override
 262                 public int size() {
 263                     return s.size();
 264                 }
 265             }
 266             db.addCollection(
 267                     c -> new AbstractSetImpl(c));
 268 
 269             class AbstractSortedSetImpl extends AbstractSet<Integer> implements SortedSet<Integer> {
 270                 SortedSet<Integer> s;
 271 
 272                 AbstractSortedSetImpl(Collection<Integer> c) {
 273                     this.s = new TreeSet<>(c);
 274                 }
 275 
 276                 @Override
 277                 public Iterator<Integer> iterator() {
 278                     return s.iterator();
 279                 }
 280 
 281                 @Override
 282                 public int size() {
 283                     return s.size();
 284                 }
 285 
 286                 @Override
 287                 public Comparator<? super Integer> comparator() {
 288                     return s.comparator();
 289                 }
 290 
 291                 @Override
 292                 public SortedSet<Integer> subSet(Integer fromElement, Integer toElement) {
 293                     return s.subSet(fromElement, toElement);
 294                 }
 295 
 296                 @Override
 297                 public SortedSet<Integer> headSet(Integer toElement) {
 298                     return s.headSet(toElement);
 299                 }
 300 
 301                 @Override
 302                 public SortedSet<Integer> tailSet(Integer fromElement) {
 303                     return s.tailSet(fromElement);
 304                 }
 305 
 306                 @Override
 307                 public Integer first() {
 308                     return s.first();
 309                 }
 310 
 311                 @Override
 312                 public Integer last() {
 313                     return s.last();
 314                 }
 315 
 316                 @Override
 317                 public Spliterator<Integer> spliterator() {
 318                     return SortedSet.super.spliterator();
 319                 }
 320             }
 321             db.addCollection(
 322                     c -> new AbstractSortedSetImpl(c));
 323 
 324             class IterableWrapper implements Iterable<Integer> {
 325                 final Iterable<Integer> it;
 326 
 327                 IterableWrapper(Iterable<Integer> it) {
 328                     this.it = it;
 329                 }
 330 
 331                 @Override
 332                 public Iterator<Integer> iterator() {
 333                     return it.iterator();
 334                 }
 335             }
 336             db.add("new Iterable.spliterator()",
 337                    () -> new IterableWrapper(exp).spliterator());
 338 
 339             //
 340 
 341             db.add("Arrays.asList().spliterator()",
 342                    () -> Spliterators.spliterator(Arrays.asList(exp.toArray(new Integer[0])), 0));
 343 
 344             db.addList(ArrayList::new);
 345 
 346             db.addList(LinkedList::new);
 347 
 348             db.addList(Vector::new);
 349 
 350 
 351             db.addCollection(HashSet::new);
 352 
 353             db.addCollection(LinkedHashSet::new);
 354 
 355             db.addCollection(TreeSet::new);
 356 
 357 
 358             db.addCollection(c -> { Stack<Integer> s = new Stack<>(); s.addAll(c); return s;});
 359 
 360             db.addCollection(PriorityQueue::new);
 361 
 362             db.addCollection(ArrayDeque::new);
 363 
 364 
 365             db.addCollection(ConcurrentSkipListSet::new);
 366 
 367             if (size > 0) {
 368                 db.addCollection(c -> {
 369                     ArrayBlockingQueue<Integer> abq = new ArrayBlockingQueue<>(size);
 370                     abq.addAll(c);
 371                     return abq;
 372                 });
 373             }
 374 
 375             db.addCollection(PriorityBlockingQueue::new);
 376 
 377             db.addCollection(LinkedBlockingQueue::new);
 378 
 379             db.addCollection(LinkedTransferQueue::new);
 380 
 381             db.addCollection(ConcurrentLinkedQueue::new);
 382 
 383             db.addCollection(LinkedBlockingDeque::new);
 384 
 385             db.addCollection(CopyOnWriteArrayList::new);
 386 
 387             db.addCollection(CopyOnWriteArraySet::new);
 388 
 389             if (size == 1) {
 390                 db.addCollection(c -> Collections.singleton(exp.get(0)));
 391                 db.addCollection(c -> Collections.singletonList(exp.get(0)));
 392             }
 393 
 394             // Collections.synchronized/unmodifiable/checked wrappers
 395             db.addCollection(Collections::unmodifiableCollection);
 396             db.addCollection(c -> Collections.unmodifiableSet(new HashSet<>(c)));
 397             db.addCollection(c -> Collections.unmodifiableSortedSet(new TreeSet<>(c)));
 398             db.addList(c -> Collections.unmodifiableList(new ArrayList<>(c)));
 399             db.addMap(Collections::unmodifiableMap);
 400             db.addMap(m -> Collections.unmodifiableSortedMap(new TreeMap<>(m)));
 401 
 402             db.addCollection(Collections::synchronizedCollection);
 403             db.addCollection(c -> Collections.synchronizedSet(new HashSet<>(c)));
 404             db.addCollection(c -> Collections.synchronizedSortedSet(new TreeSet<>(c)));
 405             db.addList(c -> Collections.synchronizedList(new ArrayList<>(c)));
 406             db.addMap(Collections::synchronizedMap);
 407             db.addMap(m -> Collections.synchronizedSortedMap(new TreeMap<>(m)));
 408 
 409             db.addCollection(c -> Collections.checkedCollection(c, Integer.class));
 410             db.addCollection(c -> Collections.checkedQueue(new ArrayDeque<>(c), Integer.class));
 411             db.addCollection(c -> Collections.checkedSet(new HashSet<>(c), Integer.class));
 412             db.addCollection(c -> Collections.checkedSortedSet(new TreeSet<>(c), Integer.class));
 413             db.addList(c -> Collections.checkedList(new ArrayList<>(c), Integer.class));
 414             db.addMap(c -> Collections.checkedMap(c, Integer.class, Integer.class));
 415             db.addMap(m -> Collections.checkedSortedMap(new TreeMap<>(m), Integer.class, Integer.class));
 416 
 417             // Maps
 418 
 419             db.addMap(HashMap::new);
 420 
 421             db.addMap(m -> {
 422                 // Create a Map ensuring that for large sizes
 423                 // buckets will contain 2 or more entries
 424                 HashMap<Integer, Integer> cm = new HashMap<>(1, m.size() + 1);
 425                 // Don't use putAll which inflates the table by
 426                 // m.size() * loadFactor, thus creating a very sparse
 427                 // map for 1000 entries defeating the purpose of this test,
 428                 // in addition it will cause the split until null test to fail
 429                 // because the number of valid splits is larger than the
 430                 // threshold
 431                 for (Map.Entry<Integer, Integer> e : m.entrySet())
 432                     cm.put(e.getKey(), e.getValue());
 433                 return cm;
 434             }, "new java.util.HashMap(1, size + 1)");
 435 
 436             db.addMap(LinkedHashMap::new);
 437 
 438             db.addMap(IdentityHashMap::new);
 439 
 440             db.addMap(WeakHashMap::new);
 441 
 442             db.addMap(m -> {
 443                 // Create a Map ensuring that for large sizes
 444                 // buckets will be consist of 2 or more entries
 445                 WeakHashMap<Integer, Integer> cm = new WeakHashMap<>(1, m.size() + 1);
 446                 for (Map.Entry<Integer, Integer> e : m.entrySet())
 447                     cm.put(e.getKey(), e.getValue());
 448                 return cm;
 449             }, "new java.util.WeakHashMap(1, size + 1)");
 450 
 451             // @@@  Descending maps etc
 452             db.addMap(TreeMap::new);
 453 
 454             db.addMap(ConcurrentHashMap::new);
 455 
 456             db.addMap(ConcurrentSkipListMap::new);
 457         }
 458 
 459         return spliteratorDataProvider = data.toArray(new Object[0][]);
 460     }
 461 
 462     private static List<Integer> listIntRange(int upTo) {
 463         List<Integer> exp = new ArrayList<>();
 464         for (int i = 0; i < upTo; i++)
 465             exp.add(i);
 466         return Collections.unmodifiableList(exp);
 467     }
 468 
 469     @Test(dataProvider = "Spliterator<Integer>")
 470     @SuppressWarnings({"unchecked", "rawtypes"})
 471     public void testNullPointerException(String description, Collection exp, Supplier<Spliterator> s) {
 472         executeAndCatch(NullPointerException.class, () -> s.get().forEachRemaining(null));
 473         executeAndCatch(NullPointerException.class, () -> s.get().tryAdvance(null));
 474     }
 475 
 476     @Test(dataProvider = "Spliterator<Integer>")
 477     @SuppressWarnings({"unchecked", "rawtypes"})
 478     public void testForEach(String description, Collection exp, Supplier<Spliterator> s) {
 479         testForEach(exp, s, (Consumer<Object> b) -> b);
 480     }
 481 
 482     @Test(dataProvider = "Spliterator<Integer>")
 483     @SuppressWarnings({"unchecked", "rawtypes"})
 484     public void testTryAdvance(String description, Collection exp, Supplier<Spliterator> s) {
 485         testTryAdvance(exp, s, (Consumer<Object> b) -> b);
 486     }
 487 
 488     @Test(dataProvider = "Spliterator<Integer>")
 489     @SuppressWarnings({"unchecked", "rawtypes"})
 490     public void testMixedTryAdvanceForEach(String description, Collection exp, Supplier<Spliterator> s) {
 491         testMixedTryAdvanceForEach(exp, s, (Consumer<Object> b) -> b);
 492     }
 493 
 494     @Test(dataProvider = "Spliterator<Integer>")
 495     @SuppressWarnings({"unchecked", "rawtypes"})
 496     public void testSplitAfterFullTraversal(String description, Collection exp, Supplier<Spliterator> s) {
 497         testSplitAfterFullTraversal(s, (Consumer<Object> b) -> b);
 498     }
 499 
 500     @Test(dataProvider = "Spliterator<Integer>")
 501     @SuppressWarnings({"unchecked", "rawtypes"})
 502     public void testSplitOnce(String description, Collection exp, Supplier<Spliterator> s) {
 503         testSplitOnce(exp, s, (Consumer<Object> b) -> b);
 504     }
 505 
 506     @Test(dataProvider = "Spliterator<Integer>")
 507     @SuppressWarnings({"unchecked", "rawtypes"})
 508     public void testSplitSixDeep(String description, Collection exp, Supplier<Spliterator> s) {
 509         testSplitSixDeep(exp, s, (Consumer<Object> b) -> b);
 510     }
 511 
 512     @Test(dataProvider = "Spliterator<Integer>")
 513     @SuppressWarnings({"unchecked", "rawtypes"})
 514     public void testSplitUntilNull(String description, Collection exp, Supplier<Spliterator> s) {
 515         testSplitUntilNull(exp, s, (Consumer<Object> b) -> b);
 516     }
 517 
 518     //
 519 
 520     private static class SpliteratorOfIntDataBuilder {
 521         List<Object[]> data;
 522 
 523         List<Integer> exp;
 524 
 525         SpliteratorOfIntDataBuilder(List<Object[]> data, List<Integer> exp) {
 526             this.data = data;
 527             this.exp = exp;
 528         }
 529 
 530         void add(String description, List<Integer> expected, Supplier<Spliterator.OfInt> s) {
 531             description = joiner(description).toString();
 532             data.add(new Object[]{description, expected, s});
 533         }
 534 
 535         void add(String description, Supplier<Spliterator.OfInt> s) {
 536             add(description, exp, s);
 537         }
 538 
 539         StringBuilder joiner(String description) {
 540             return new StringBuilder(description).
 541                     append(" {").
 542                     append("size=").append(exp.size()).
 543                     append("}");
 544         }
 545     }
 546 
 547     static Object[][] spliteratorOfIntDataProvider;
 548 
 549     @DataProvider(name = "Spliterator.OfInt")
 550     public static Object[][] spliteratorOfIntDataProvider() {
 551         if (spliteratorOfIntDataProvider != null) {
 552             return spliteratorOfIntDataProvider;
 553         }
 554 
 555         List<Object[]> data = new ArrayList<>();
 556         for (int size : SIZES) {
 557             int exp[] = arrayIntRange(size);
 558             SpliteratorOfIntDataBuilder db = new SpliteratorOfIntDataBuilder(data, listIntRange(size));
 559 
 560             db.add("Spliterators.spliterator(int[], ...)",
 561                    () -> Spliterators.spliterator(exp, 0));
 562 
 563             db.add("Arrays.spliterator(int[], ...)",
 564                    () -> Arrays.spliterator(exp));
 565 
 566             db.add("Spliterators.spliterator(PrimitiveIterator.OfInt, ...)",
 567                    () -> Spliterators.spliterator(Spliterators.iterator(Arrays.spliterator(exp)), exp.length, 0));
 568 
 569             db.add("Spliterators.spliteratorUnknownSize(PrimitiveIterator.OfInt, ...)",
 570                    () -> Spliterators.spliteratorUnknownSize(Spliterators.iterator(Arrays.spliterator(exp)), 0));
 571 
 572             class IntSpliteratorFromArray extends Spliterators.AbstractIntSpliterator {
 573                 int[] a;
 574                 int index = 0;
 575 
 576                 IntSpliteratorFromArray(int[] a) {
 577                     super(a.length, Spliterator.SIZED);
 578                     this.a = a;
 579                 }
 580 
 581                 @Override
 582                 public boolean tryAdvance(IntConsumer action) {
 583                     if (action == null)
 584                         throw new NullPointerException();
 585                     if (index < a.length) {
 586                         action.accept(a[index++]);
 587                         return true;
 588                     }
 589                     else {
 590                         return false;
 591                     }
 592                 }
 593             }
 594             db.add("new Spliterators.AbstractIntAdvancingSpliterator()",
 595                    () -> new IntSpliteratorFromArray(exp));
 596         }
 597 
 598         return spliteratorOfIntDataProvider = data.toArray(new Object[0][]);
 599     }
 600 
 601     private static int[] arrayIntRange(int upTo) {
 602         int[] exp = new int[upTo];
 603         for (int i = 0; i < upTo; i++)
 604             exp[i] = i;
 605         return exp;
 606     }
 607 
 608     private static UnaryOperator<Consumer<Integer>> intBoxingConsumer() {
 609         class BoxingAdapter implements Consumer<Integer>, IntConsumer {
 610             private final Consumer<Integer> b;
 611 
 612             BoxingAdapter(Consumer<Integer> b) {
 613                 this.b = b;
 614             }
 615 
 616             @Override
 617             public void accept(Integer value) {
 618                 throw new IllegalStateException();
 619             }
 620 
 621             @Override
 622             public void accept(int value) {
 623                 b.accept(value);
 624             }
 625         }
 626 
 627         return b -> new BoxingAdapter(b);
 628     }
 629 
 630     @Test(dataProvider = "Spliterator.OfInt")
 631     public void testIntNullPointerException(String description, Collection<Integer> exp, Supplier<Spliterator.OfInt> s) {
 632         executeAndCatch(NullPointerException.class, () -> s.get().forEachRemaining((IntConsumer) null));
 633         executeAndCatch(NullPointerException.class, () -> s.get().tryAdvance((IntConsumer) null));
 634     }
 635 
 636     @Test(dataProvider = "Spliterator.OfInt")
 637     public void testIntForEach(String description, Collection<Integer> exp, Supplier<Spliterator.OfInt> s) {
 638         testForEach(exp, s, intBoxingConsumer());
 639     }
 640 
 641     @Test(dataProvider = "Spliterator.OfInt")
 642     public void testIntTryAdvance(String description, Collection<Integer> exp, Supplier<Spliterator.OfInt> s) {
 643         testTryAdvance(exp, s, intBoxingConsumer());
 644     }
 645 
 646     @Test(dataProvider = "Spliterator.OfInt")
 647     public void testIntMixedTryAdvanceForEach(String description, Collection<Integer> exp, Supplier<Spliterator.OfInt> s) {
 648         testMixedTryAdvanceForEach(exp, s, intBoxingConsumer());
 649     }
 650 
 651     @Test(dataProvider = "Spliterator.OfInt")
 652     public void testIntSplitAfterFullTraversal(String description, Collection<Integer> exp, Supplier<Spliterator.OfInt> s) {
 653         testSplitAfterFullTraversal(s, intBoxingConsumer());
 654     }
 655 
 656     @Test(dataProvider = "Spliterator.OfInt")
 657     public void testIntSplitOnce(String description, Collection<Integer> exp, Supplier<Spliterator.OfInt> s) {
 658         testSplitOnce(exp, s, intBoxingConsumer());
 659     }
 660 
 661     @Test(dataProvider = "Spliterator.OfInt")
 662     public void testIntSplitSixDeep(String description, Collection<Integer> exp, Supplier<Spliterator.OfInt> s) {
 663         testSplitSixDeep(exp, s, intBoxingConsumer());
 664     }
 665 
 666     @Test(dataProvider = "Spliterator.OfInt")
 667     public void testIntSplitUntilNull(String description, Collection<Integer> exp, Supplier<Spliterator.OfInt> s) {
 668         testSplitUntilNull(exp, s, intBoxingConsumer());
 669     }
 670 
 671     //
 672 
 673     private static class SpliteratorOfLongDataBuilder {
 674         List<Object[]> data;
 675 
 676         List<Long> exp;
 677 
 678         SpliteratorOfLongDataBuilder(List<Object[]> data, List<Long> exp) {
 679             this.data = data;
 680             this.exp = exp;
 681         }
 682 
 683         void add(String description, List<Long> expected, Supplier<Spliterator.OfLong> s) {
 684             description = joiner(description).toString();
 685             data.add(new Object[]{description, expected, s});
 686         }
 687 
 688         void add(String description, Supplier<Spliterator.OfLong> s) {
 689             add(description, exp, s);
 690         }
 691 
 692         StringBuilder joiner(String description) {
 693             return new StringBuilder(description).
 694                     append(" {").
 695                     append("size=").append(exp.size()).
 696                     append("}");
 697         }
 698     }
 699 
 700     static Object[][] spliteratorOfLongDataProvider;
 701 
 702     @DataProvider(name = "Spliterator.OfLong")
 703     public static Object[][] spliteratorOfLongDataProvider() {
 704         if (spliteratorOfLongDataProvider != null) {
 705             return spliteratorOfLongDataProvider;
 706         }
 707 
 708         List<Object[]> data = new ArrayList<>();
 709         for (int size : SIZES) {
 710             long exp[] = arrayLongRange(size);
 711             SpliteratorOfLongDataBuilder db = new SpliteratorOfLongDataBuilder(data, listLongRange(size));
 712 
 713             db.add("Spliterators.spliterator(long[], ...)",
 714                    () -> Spliterators.spliterator(exp, 0));
 715 
 716             db.add("Arrays.spliterator(long[], ...)",
 717                    () -> Arrays.spliterator(exp));
 718 
 719             db.add("Spliterators.spliterator(PrimitiveIterator.OfLong, ...)",
 720                    () -> Spliterators.spliterator(Spliterators.iterator(Arrays.spliterator(exp)), exp.length, 0));
 721 
 722             db.add("Spliterators.spliteratorUnknownSize(PrimitiveIterator.OfLong, ...)",
 723                    () -> Spliterators.spliteratorUnknownSize(Spliterators.iterator(Arrays.spliterator(exp)), 0));
 724 
 725             class LongSpliteratorFromArray extends Spliterators.AbstractLongSpliterator {
 726                 long[] a;
 727                 int index = 0;
 728 
 729                 LongSpliteratorFromArray(long[] a) {
 730                     super(a.length, Spliterator.SIZED);
 731                     this.a = a;
 732                 }
 733 
 734                 @Override
 735                 public boolean tryAdvance(LongConsumer action) {
 736                     if (action == null)
 737                         throw new NullPointerException();
 738                     if (index < a.length) {
 739                         action.accept(a[index++]);
 740                         return true;
 741                     }
 742                     else {
 743                         return false;
 744                     }
 745                 }
 746             }
 747             db.add("new Spliterators.AbstractLongAdvancingSpliterator()",
 748                    () -> new LongSpliteratorFromArray(exp));
 749         }
 750 
 751         return spliteratorOfLongDataProvider = data.toArray(new Object[0][]);
 752     }
 753 
 754     private static List<Long> listLongRange(int upTo) {
 755         List<Long> exp = new ArrayList<>();
 756         for (long i = 0; i < upTo; i++)
 757             exp.add(i);
 758         return Collections.unmodifiableList(exp);
 759     }
 760 
 761     private static long[] arrayLongRange(int upTo) {
 762         long[] exp = new long[upTo];
 763         for (int i = 0; i < upTo; i++)
 764             exp[i] = i;
 765         return exp;
 766     }
 767 
 768     private static UnaryOperator<Consumer<Long>> longBoxingConsumer() {
 769         class BoxingAdapter implements Consumer<Long>, LongConsumer {
 770             private final Consumer<Long> b;
 771 
 772             BoxingAdapter(Consumer<Long> b) {
 773                 this.b = b;
 774             }
 775 
 776             @Override
 777             public void accept(Long value) {
 778                 throw new IllegalStateException();
 779             }
 780 
 781             @Override
 782             public void accept(long value) {
 783                 b.accept(value);
 784             }
 785         }
 786 
 787         return b -> new BoxingAdapter(b);
 788     }
 789 
 790     @Test(dataProvider = "Spliterator.OfLong")
 791     public void testLongNullPointerException(String description, Collection<Long> exp, Supplier<Spliterator.OfLong> s) {
 792         executeAndCatch(NullPointerException.class, () -> s.get().forEachRemaining((LongConsumer) null));
 793         executeAndCatch(NullPointerException.class, () -> s.get().tryAdvance((LongConsumer) null));
 794     }
 795 
 796     @Test(dataProvider = "Spliterator.OfLong")
 797     public void testLongForEach(String description, Collection<Long> exp, Supplier<Spliterator.OfLong> s) {
 798         testForEach(exp, s, longBoxingConsumer());
 799     }
 800 
 801     @Test(dataProvider = "Spliterator.OfLong")
 802     public void testLongTryAdvance(String description, Collection<Long> exp, Supplier<Spliterator.OfLong> s) {
 803         testTryAdvance(exp, s, longBoxingConsumer());
 804     }
 805 
 806     @Test(dataProvider = "Spliterator.OfLong")
 807     public void testLongMixedTryAdvanceForEach(String description, Collection<Long> exp, Supplier<Spliterator.OfLong> s) {
 808         testMixedTryAdvanceForEach(exp, s, longBoxingConsumer());
 809     }
 810 
 811     @Test(dataProvider = "Spliterator.OfLong")
 812     public void testLongSplitAfterFullTraversal(String description, Collection<Long> exp, Supplier<Spliterator.OfLong> s) {
 813         testSplitAfterFullTraversal(s, longBoxingConsumer());
 814     }
 815 
 816     @Test(dataProvider = "Spliterator.OfLong")
 817     public void testLongSplitOnce(String description, Collection<Long> exp, Supplier<Spliterator.OfLong> s) {
 818         testSplitOnce(exp, s, longBoxingConsumer());
 819     }
 820 
 821     @Test(dataProvider = "Spliterator.OfLong")
 822     public void testLongSplitSixDeep(String description, Collection<Long> exp, Supplier<Spliterator.OfLong> s) {
 823         testSplitSixDeep(exp, s, longBoxingConsumer());
 824     }
 825 
 826     @Test(dataProvider = "Spliterator.OfLong")
 827     public void testLongSplitUntilNull(String description, Collection<Long> exp, Supplier<Spliterator.OfLong> s) {
 828         testSplitUntilNull(exp, s, longBoxingConsumer());
 829     }
 830 
 831     //
 832 
 833     private static class SpliteratorOfDoubleDataBuilder {
 834         List<Object[]> data;
 835 
 836         List<Double> exp;
 837 
 838         SpliteratorOfDoubleDataBuilder(List<Object[]> data, List<Double> exp) {
 839             this.data = data;
 840             this.exp = exp;
 841         }
 842 
 843         void add(String description, List<Double> expected, Supplier<Spliterator.OfDouble> s) {
 844             description = joiner(description).toString();
 845             data.add(new Object[]{description, expected, s});
 846         }
 847 
 848         void add(String description, Supplier<Spliterator.OfDouble> s) {
 849             add(description, exp, s);
 850         }
 851 
 852         StringBuilder joiner(String description) {
 853             return new StringBuilder(description).
 854                     append(" {").
 855                     append("size=").append(exp.size()).
 856                     append("}");
 857         }
 858     }
 859 
 860     static Object[][] spliteratorOfDoubleDataProvider;
 861 
 862     @DataProvider(name = "Spliterator.OfDouble")
 863     public static Object[][] spliteratorOfDoubleDataProvider() {
 864         if (spliteratorOfDoubleDataProvider != null) {
 865             return spliteratorOfDoubleDataProvider;
 866         }
 867 
 868         List<Object[]> data = new ArrayList<>();
 869         for (int size : SIZES) {
 870             double exp[] = arrayDoubleRange(size);
 871             SpliteratorOfDoubleDataBuilder db = new SpliteratorOfDoubleDataBuilder(data, listDoubleRange(size));
 872 
 873             db.add("Spliterators.spliterator(double[], ...)",
 874                    () -> Spliterators.spliterator(exp, 0));
 875 
 876             db.add("Arrays.spliterator(double[], ...)",
 877                    () -> Arrays.spliterator(exp));
 878 
 879             db.add("Spliterators.spliterator(PrimitiveIterator.OfDouble, ...)",
 880                    () -> Spliterators.spliterator(Spliterators.iterator(Arrays.spliterator(exp)), exp.length, 0));
 881 
 882             db.add("Spliterators.spliteratorUnknownSize(PrimitiveIterator.OfDouble, ...)",
 883                    () -> Spliterators.spliteratorUnknownSize(Spliterators.iterator(Arrays.spliterator(exp)), 0));
 884 
 885             class DoubleSpliteratorFromArray extends Spliterators.AbstractDoubleSpliterator {
 886                 double[] a;
 887                 int index = 0;
 888 
 889                 DoubleSpliteratorFromArray(double[] a) {
 890                     super(a.length, Spliterator.SIZED);
 891                     this.a = a;
 892                 }
 893 
 894                 @Override
 895                 public boolean tryAdvance(DoubleConsumer action) {
 896                     if (action == null)
 897                         throw new NullPointerException();
 898                     if (index < a.length) {
 899                         action.accept(a[index++]);
 900                         return true;
 901                     }
 902                     else {
 903                         return false;
 904                     }
 905                 }
 906             }
 907             db.add("new Spliterators.AbstractDoubleAdvancingSpliterator()",
 908                    () -> new DoubleSpliteratorFromArray(exp));
 909         }
 910 
 911         return spliteratorOfDoubleDataProvider = data.toArray(new Object[0][]);
 912     }
 913 
 914     private static List<Double> listDoubleRange(int upTo) {
 915         List<Double> exp = new ArrayList<>();
 916         for (double i = 0; i < upTo; i++)
 917             exp.add(i);
 918         return Collections.unmodifiableList(exp);
 919     }
 920 
 921     private static double[] arrayDoubleRange(int upTo) {
 922         double[] exp = new double[upTo];
 923         for (int i = 0; i < upTo; i++)
 924             exp[i] = i;
 925         return exp;
 926     }
 927 
 928     private static UnaryOperator<Consumer<Double>> doubleBoxingConsumer() {
 929         class BoxingAdapter implements Consumer<Double>, DoubleConsumer {
 930             private final Consumer<Double> b;
 931 
 932             BoxingAdapter(Consumer<Double> b) {
 933                 this.b = b;
 934             }
 935 
 936             @Override
 937             public void accept(Double value) {
 938                 throw new IllegalStateException();
 939             }
 940 
 941             @Override
 942             public void accept(double value) {
 943                 b.accept(value);
 944             }
 945         }
 946 
 947         return b -> new BoxingAdapter(b);
 948     }
 949 
 950     @Test(dataProvider = "Spliterator.OfDouble")
 951     public void testDoubleNullPointerException(String description, Collection<Double> exp, Supplier<Spliterator.OfDouble> s) {
 952         executeAndCatch(NullPointerException.class, () -> s.get().forEachRemaining((DoubleConsumer) null));
 953         executeAndCatch(NullPointerException.class, () -> s.get().tryAdvance((DoubleConsumer) null));
 954     }
 955 
 956     @Test(dataProvider = "Spliterator.OfDouble")
 957     public void testDoubleForEach(String description, Collection<Double> exp, Supplier<Spliterator.OfDouble> s) {
 958         testForEach(exp, s, doubleBoxingConsumer());
 959     }
 960 
 961     @Test(dataProvider = "Spliterator.OfDouble")
 962     public void testDoubleTryAdvance(String description, Collection<Double> exp, Supplier<Spliterator.OfDouble> s) {
 963         testTryAdvance(exp, s, doubleBoxingConsumer());
 964     }
 965 
 966     @Test(dataProvider = "Spliterator.OfDouble")
 967     public void testDoubleMixedTryAdvanceForEach(String description, Collection<Double> exp, Supplier<Spliterator.OfDouble> s) {
 968         testMixedTryAdvanceForEach(exp, s, doubleBoxingConsumer());
 969     }
 970 
 971     @Test(dataProvider = "Spliterator.OfDouble")
 972     public void testDoubleSplitAfterFullTraversal(String description, Collection<Double> exp, Supplier<Spliterator.OfDouble> s) {
 973         testSplitAfterFullTraversal(s, doubleBoxingConsumer());
 974     }
 975 
 976     @Test(dataProvider = "Spliterator.OfDouble")
 977     public void testDoubleSplitOnce(String description, Collection<Double> exp, Supplier<Spliterator.OfDouble> s) {
 978         testSplitOnce(exp, s, doubleBoxingConsumer());
 979     }
 980 
 981     @Test(dataProvider = "Spliterator.OfDouble")
 982     public void testDoubleSplitSixDeep(String description, Collection<Double> exp, Supplier<Spliterator.OfDouble> s) {
 983         testSplitSixDeep(exp, s, doubleBoxingConsumer());
 984     }
 985 
 986     @Test(dataProvider = "Spliterator.OfDouble")
 987     public void testDoubleSplitUntilNull(String description, Collection<Double> exp, Supplier<Spliterator.OfDouble> s) {
 988         testSplitUntilNull(exp, s, doubleBoxingConsumer());
 989     }
 990 
 991     //
 992 
 993     private static <T, S extends Spliterator<T>> void testForEach(
 994             Collection<T> exp,
 995             Supplier<S> supplier,
 996             UnaryOperator<Consumer<T>> boxingAdapter) {
 997         S spliterator = supplier.get();
 998         long sizeIfKnown = spliterator.getExactSizeIfKnown();
 999         boolean isOrdered = spliterator.hasCharacteristics(Spliterator.ORDERED);
1000 
1001         ArrayList<T> fromForEach = new ArrayList<>();
1002         spliterator = supplier.get();
1003         Consumer<T> addToFromForEach = boxingAdapter.apply(fromForEach::add);
1004         spliterator.forEachRemaining(addToFromForEach);
1005 
1006         // Assert that forEach now produces no elements
1007         spliterator.forEachRemaining(boxingAdapter.apply(e -> fail("Spliterator.forEach produced an element after spliterator exhausted: " + e)));
1008         // Assert that tryAdvance now produce no elements
1009         spliterator.tryAdvance(boxingAdapter.apply(e -> fail("Spliterator.tryAdvance produced an element after spliterator exhausted: " + e)));
1010 
1011         // assert that size, tryAdvance, and forEach are consistent
1012         if (sizeIfKnown >= 0) {
1013             assertEquals(sizeIfKnown, exp.size());
1014         }
1015         assertEquals(fromForEach.size(), exp.size());
1016 
1017         assertContents(fromForEach, exp, isOrdered);
1018     }
1019 
1020     private static <T, S extends Spliterator<T>> void testTryAdvance(
1021             Collection<T> exp,
1022             Supplier<S> supplier,
1023             UnaryOperator<Consumer<T>> boxingAdapter) {
1024         S spliterator = supplier.get();
1025         long sizeIfKnown = spliterator.getExactSizeIfKnown();
1026         boolean isOrdered = spliterator.hasCharacteristics(Spliterator.ORDERED);
1027 
1028         spliterator = supplier.get();
1029         ArrayList<T> fromTryAdvance = new ArrayList<>();
1030         Consumer<T> addToFromTryAdvance = boxingAdapter.apply(fromTryAdvance::add);
1031         while (spliterator.tryAdvance(addToFromTryAdvance)) { }
1032 
1033         // Assert that forEach now produces no elements
1034         spliterator.forEachRemaining(boxingAdapter.apply(e -> fail("Spliterator.forEach produced an element after spliterator exhausted: " + e)));
1035         // Assert that tryAdvance now produce no elements
1036         spliterator.tryAdvance(boxingAdapter.apply(e -> fail("Spliterator.tryAdvance produced an element after spliterator exhausted: " + e)));
1037 
1038         // assert that size, tryAdvance, and forEach are consistent
1039         if (sizeIfKnown >= 0) {
1040             assertEquals(sizeIfKnown, exp.size());
1041         }
1042         assertEquals(fromTryAdvance.size(), exp.size());
1043 
1044         assertContents(fromTryAdvance, exp, isOrdered);
1045     }
1046 
1047     private static <T, S extends Spliterator<T>> void testMixedTryAdvanceForEach(
1048             Collection<T> exp,
1049             Supplier<S> supplier,
1050             UnaryOperator<Consumer<T>> boxingAdapter) {
1051         S spliterator = supplier.get();
1052         long sizeIfKnown = spliterator.getExactSizeIfKnown();
1053         boolean isOrdered = spliterator.hasCharacteristics(Spliterator.ORDERED);
1054 
1055         // tryAdvance first few elements, then forEach rest
1056         ArrayList<T> dest = new ArrayList<>();
1057         spliterator = supplier.get();
1058         Consumer<T> addToDest = boxingAdapter.apply(dest::add);
1059         for (int i = 0; i < 10 && spliterator.tryAdvance(addToDest); i++) { }
1060         spliterator.forEachRemaining(addToDest);
1061 
1062         // Assert that forEach now produces no elements
1063         spliterator.forEachRemaining(boxingAdapter.apply(e -> fail("Spliterator.forEach produced an element after spliterator exhausted: " + e)));
1064         // Assert that tryAdvance now produce no elements
1065         spliterator.tryAdvance(boxingAdapter.apply(e -> fail("Spliterator.tryAdvance produced an element after spliterator exhausted: " + e)));
1066 
1067         if (sizeIfKnown >= 0) {
1068             assertEquals(sizeIfKnown, dest.size());
1069         }
1070         assertEquals(dest.size(), exp.size());
1071 
1072         if (isOrdered) {
1073             assertEquals(dest, exp);
1074         }
1075         else {
1076             assertContentsUnordered(dest, exp);
1077         }
1078     }
1079 
1080     private static <T, S extends Spliterator<T>> void testSplitAfterFullTraversal(
1081             Supplier<S> supplier,
1082             UnaryOperator<Consumer<T>> boxingAdapter) {
1083         // Full traversal using tryAdvance
1084         Spliterator<T> spliterator = supplier.get();
1085         while (spliterator.tryAdvance(boxingAdapter.apply(e -> { }))) { }
1086         Spliterator<T> split = spliterator.trySplit();
1087         assertNull(split);
1088 
1089         // Full traversal using forEach
1090         spliterator = supplier.get();
1091         spliterator.forEachRemaining(boxingAdapter.apply(e -> {
1092         }));
1093         split = spliterator.trySplit();
1094         assertNull(split);
1095 
1096         // Full traversal using tryAdvance then forEach
1097         spliterator = supplier.get();
1098         spliterator.tryAdvance(boxingAdapter.apply(e -> { }));
1099         spliterator.forEachRemaining(boxingAdapter.apply(e -> {
1100         }));
1101         split = spliterator.trySplit();
1102         assertNull(split);
1103     }
1104 
1105     private static <T, S extends Spliterator<T>> void testSplitOnce(
1106             Collection<T> exp,
1107             Supplier<S> supplier,
1108             UnaryOperator<Consumer<T>> boxingAdapter) {
1109         S spliterator = supplier.get();
1110         long sizeIfKnown = spliterator.getExactSizeIfKnown();
1111         boolean isOrdered = spliterator.hasCharacteristics(Spliterator.ORDERED);
1112 
1113         ArrayList<T> fromSplit = new ArrayList<>();
1114         Spliterator<T> s1 = supplier.get();
1115         Spliterator<T> s2 = s1.trySplit();
1116         long s1Size = s1.getExactSizeIfKnown();
1117         long s2Size = (s2 != null) ? s2.getExactSizeIfKnown() : 0;
1118         Consumer<T> addToFromSplit = boxingAdapter.apply(fromSplit::add);
1119         if (s2 != null)
1120             s2.forEachRemaining(addToFromSplit);
1121         s1.forEachRemaining(addToFromSplit);
1122 
1123         if (sizeIfKnown >= 0) {
1124             assertEquals(sizeIfKnown, fromSplit.size());
1125             if (s1Size >= 0 && s2Size >= 0)
1126                 assertEquals(sizeIfKnown, s1Size + s2Size);
1127         }
1128         assertContents(fromSplit, exp, isOrdered);
1129     }
1130 
1131     private static <T, S extends Spliterator<T>> void testSplitSixDeep(
1132             Collection<T> exp,
1133             Supplier<S> supplier,
1134             UnaryOperator<Consumer<T>> boxingAdapter) {
1135         S spliterator = supplier.get();
1136         boolean isOrdered = spliterator.hasCharacteristics(Spliterator.ORDERED);
1137 
1138         for (int depth=0; depth < 6; depth++) {
1139             List<T> dest = new ArrayList<>();
1140             spliterator = supplier.get();
1141 
1142             assertSpliterator(spliterator);
1143 
1144             // verify splitting with forEach
1145             visit(depth, 0, dest, spliterator, boxingAdapter, spliterator.characteristics(), false);
1146             assertContents(dest, exp, isOrdered);
1147 
1148             // verify splitting with tryAdvance
1149             dest.clear();
1150             spliterator = supplier.get();
1151             visit(depth, 0, dest, spliterator, boxingAdapter, spliterator.characteristics(), true);
1152             assertContents(dest, exp, isOrdered);
1153         }
1154     }
1155 
1156     private static <T, S extends Spliterator<T>> void visit(int depth, int curLevel,
1157                                                             List<T> dest, S spliterator, UnaryOperator<Consumer<T>> boxingAdapter,
1158                                                             int rootCharacteristics, boolean useTryAdvance) {
1159         if (curLevel < depth) {
1160             long beforeSize = spliterator.getExactSizeIfKnown();
1161             Spliterator<T> split = spliterator.trySplit();
1162             if (split != null) {
1163                 assertSpliterator(split, rootCharacteristics);
1164                 assertSpliterator(spliterator, rootCharacteristics);
1165 
1166                 if ((rootCharacteristics & Spliterator.SUBSIZED) != 0 &&
1167                     (rootCharacteristics & Spliterator.SIZED) != 0) {
1168                     assertEquals(beforeSize, split.estimateSize() + spliterator.estimateSize());
1169                 }
1170                 visit(depth, curLevel + 1, dest, split, boxingAdapter, rootCharacteristics, useTryAdvance);
1171             }
1172             visit(depth, curLevel + 1, dest, spliterator, boxingAdapter, rootCharacteristics, useTryAdvance);
1173         }
1174         else {
1175             long sizeIfKnown = spliterator.getExactSizeIfKnown();
1176             if (useTryAdvance) {
1177                 Consumer<T> addToDest = boxingAdapter.apply(dest::add);
1178                 int count = 0;
1179                 while (spliterator.tryAdvance(addToDest)) {
1180                     ++count;
1181                 }
1182 
1183                 if (sizeIfKnown >= 0)
1184                     assertEquals(sizeIfKnown, count);
1185 
1186                 // Assert that forEach now produces no elements
1187                 spliterator.forEachRemaining(boxingAdapter.apply(e -> fail("Spliterator.forEach produced an element after spliterator exhausted: " + e)));
1188 
1189                 Spliterator<T> split = spliterator.trySplit();
1190                 assertNull(split);
1191             }
1192             else {
1193                 List<T> leafDest = new ArrayList<>();
1194                 Consumer<T> addToLeafDest = boxingAdapter.apply(leafDest::add);
1195                 spliterator.forEachRemaining(addToLeafDest);
1196 
1197                 if (sizeIfKnown >= 0)
1198                     assertEquals(sizeIfKnown, leafDest.size());
1199 
1200                 // Assert that forEach now produces no elements
1201                 spliterator.tryAdvance(boxingAdapter.apply(e -> fail("Spliterator.tryAdvance produced an element after spliterator exhausted: " + e)));
1202 
1203                 Spliterator<T> split = spliterator.trySplit();
1204                 assertNull(split);
1205 
1206                 dest.addAll(leafDest);
1207             }
1208         }
1209     }
1210 
1211     private static <T, S extends Spliterator<T>> void testSplitUntilNull(
1212             Collection<T> exp,
1213             Supplier<S> supplier,
1214             UnaryOperator<Consumer<T>> boxingAdapter) {
1215         Spliterator<T> s = supplier.get();
1216         boolean isOrdered = s.hasCharacteristics(Spliterator.ORDERED);
1217         assertSpliterator(s);
1218 
1219         List<T> splits = new ArrayList<>();
1220         Consumer<T> c = boxingAdapter.apply(splits::add);
1221 
1222         testSplitUntilNull(new SplitNode<T>(c, s));
1223         assertContents(splits, exp, isOrdered);
1224     }
1225 
1226     private static class SplitNode<T> {
1227         // Constant for every node
1228         final Consumer<T> c;
1229         final int rootCharacteristics;
1230 
1231         final Spliterator<T> s;
1232 
1233         SplitNode(Consumer<T> c, Spliterator<T> s) {
1234             this(c, s.characteristics(), s);
1235         }
1236 
1237         private SplitNode(Consumer<T> c, int rootCharacteristics, Spliterator<T> s) {
1238             this.c = c;
1239             this.rootCharacteristics = rootCharacteristics;
1240             this.s = s;
1241         }
1242 
1243         SplitNode<T> fromSplit(Spliterator<T> split) {
1244             return new SplitNode<>(c, rootCharacteristics, split);
1245         }
1246     }
1247 
1248     /**
1249      * Set the maximum stack capacity to 0.25MB. This should be more than enough to detect a bad spliterator
1250      * while not unduly disrupting test infrastructure given the test data sizes that are used are small.
1251      * Note that j.u.c.ForkJoinPool sets the max queue size to 64M (1 << 26).
1252      */
1253     private static final int MAXIMUM_STACK_CAPACITY = 1 << 18; // 0.25MB
1254 
1255     private static <T> void testSplitUntilNull(SplitNode<T> e) {
1256         // Use an explicit stack to avoid a StackOverflowException when testing a Spliterator
1257         // that when repeatedly split produces a right-balanced (and maybe degenerate) tree, or
1258         // for a spliterator that is badly behaved.
1259         Deque<SplitNode<T>> stack = new ArrayDeque<>();
1260         stack.push(e);
1261 
1262         int iteration = 0;
1263         while (!stack.isEmpty()) {
1264             assertTrue(iteration++ < MAXIMUM_STACK_CAPACITY, "Exceeded maximum stack modification count of 1 << 18");
1265 
1266             e = stack.pop();
1267             Spliterator<T> parentAndRightSplit = e.s;
1268 
1269             long parentEstimateSize = parentAndRightSplit.estimateSize();
1270             assertTrue(parentEstimateSize >= 0,
1271                        String.format("Split size estimate %d < 0", parentEstimateSize));
1272 
1273             long parentSize = parentAndRightSplit.getExactSizeIfKnown();
1274             Spliterator<T> leftSplit = parentAndRightSplit.trySplit();
1275             if (leftSplit == null) {
1276                 parentAndRightSplit.forEachRemaining(e.c);
1277                 continue;
1278             }
1279 
1280             assertSpliterator(leftSplit, e.rootCharacteristics);
1281             assertSpliterator(parentAndRightSplit, e.rootCharacteristics);
1282 
1283             if (parentEstimateSize != Long.MAX_VALUE && leftSplit.estimateSize() > 0 && parentAndRightSplit.estimateSize() > 0) {
1284                 assertTrue(leftSplit.estimateSize() < parentEstimateSize,
1285                            String.format("Left split size estimate %d >= parent split size estimate %d", leftSplit.estimateSize(), parentEstimateSize));
1286                 assertTrue(parentAndRightSplit.estimateSize() < parentEstimateSize,
1287                            String.format("Right split size estimate %d >= parent split size estimate %d", leftSplit.estimateSize(), parentEstimateSize));
1288             }
1289             else {
1290                 assertTrue(leftSplit.estimateSize() <= parentEstimateSize,
1291                            String.format("Left split size estimate %d > parent split size estimate %d", leftSplit.estimateSize(), parentEstimateSize));
1292                 assertTrue(parentAndRightSplit.estimateSize() <= parentEstimateSize,
1293                            String.format("Right split size estimate %d > parent split size estimate %d", leftSplit.estimateSize(), parentEstimateSize));
1294             }
1295 
1296             long leftSize = leftSplit.getExactSizeIfKnown();
1297             long rightSize = parentAndRightSplit.getExactSizeIfKnown();
1298             if (parentSize >= 0 && leftSize >= 0 && rightSize >= 0)
1299                 assertEquals(parentSize, leftSize + rightSize,
1300                              String.format("exact left split size %d + exact right split size %d != parent exact split size %d",
1301                                            leftSize, rightSize, parentSize));
1302 
1303             // Add right side to stack first so left side is popped off first
1304             stack.push(e.fromSplit(parentAndRightSplit));
1305             stack.push(e.fromSplit(leftSplit));
1306         }
1307     }
1308 
1309     private static void assertSpliterator(Spliterator<?> s, int rootCharacteristics) {
1310         if ((rootCharacteristics & Spliterator.SUBSIZED) != 0) {
1311             assertTrue(s.hasCharacteristics(Spliterator.SUBSIZED),
1312                        "Child split is not SUBSIZED when root split is SUBSIZED");
1313         }
1314         assertSpliterator(s);
1315     }
1316 
1317     private static void assertSpliterator(Spliterator<?> s) {
1318         if (s.hasCharacteristics(Spliterator.SUBSIZED)) {
1319             assertTrue(s.hasCharacteristics(Spliterator.SIZED));
1320         }
1321         if (s.hasCharacteristics(Spliterator.SIZED)) {
1322             assertTrue(s.estimateSize() != Long.MAX_VALUE);
1323             assertTrue(s.getExactSizeIfKnown() >= 0);
1324         }
1325         try {
1326             s.getComparator();
1327             assertTrue(s.hasCharacteristics(Spliterator.SORTED));
1328         } catch (IllegalStateException e) {
1329             assertFalse(s.hasCharacteristics(Spliterator.SORTED));
1330         }
1331     }
1332 
1333     private static<T> void assertContents(Collection<T> actual, Collection<T> expected, boolean isOrdered) {
1334         if (isOrdered) {
1335             assertEquals(actual, expected);
1336         }
1337         else {
1338             assertContentsUnordered(actual, expected);
1339         }
1340     }
1341 
1342     private static<T> void assertContentsUnordered(Iterable<T> actual, Iterable<T> expected) {
1343         assertEquals(toBoxedMultiset(actual), toBoxedMultiset(expected));
1344     }
1345 
1346     private static <T> Map<T, Integer> toBoxedMultiset(Iterable<T> c) {
1347         Map<T, Integer> result = new HashMap<>();
1348         c.forEach(e -> {
1349             if (result.containsKey(e)) result.put(e, result.get(e) + 1);
1350             else result.put(e, 1);
1351         });
1352         return result;
1353     }
1354 
1355     private void executeAndCatch(Class<? extends Exception> expected, Runnable r) {
1356         Exception caught = null;
1357         try {
1358             r.run();
1359         }
1360         catch (Exception e) {
1361             caught = e;
1362         }
1363 
1364         assertNotNull(caught,
1365                       String.format("No Exception was thrown, expected an Exception of %s to be thrown",
1366                                     expected.getName()));
1367         assertTrue(expected.isInstance(caught),
1368                    String.format("Exception thrown %s not an instance of %s",
1369                                  caught.getClass().getName(), expected.getName()));
1370     }
1371 
1372 }