From ca77f42179b4b0f5d97782e99f817e491766bcfe Mon Sep 17 00:00:00 2001 From: Paul Sandoz Date: Thu, 1 Dec 2016 17:52:59 -0800 Subject: [PATCH] 8170155: StringBuffer and StringBuilder stream methods are not late-binding Reviewed-by: sherman --- .../java/lang/AbstractStringBuilder.java | 30 +- .../share/classes/java/lang/StringUTF16.java | 18 +- jdk/test/TEST.groups | 3 +- .../Spliterator/SpliteratorFailFastTest.java | 201 +++++++++ .../SpliteratorLateBindingFailFastHelper.java | 224 ++++++++++ .../SpliteratorLateBindingFailFastTest.java | 401 ------------------ .../SpliteratorLateBindingTest.java | 221 ++++++++++ ...SpliteratorTraversingAndSplittingTest.java | 2 + 8 files changed, 683 insertions(+), 417 deletions(-) create mode 100644 jdk/test/java/util/Spliterator/SpliteratorFailFastTest.java create mode 100644 jdk/test/java/util/Spliterator/SpliteratorLateBindingFailFastHelper.java delete mode 100644 jdk/test/java/util/Spliterator/SpliteratorLateBindingFailFastTest.java create mode 100644 jdk/test/java/util/Spliterator/SpliteratorLateBindingTest.java diff --git a/jdk/src/java.base/share/classes/java/lang/AbstractStringBuilder.java b/jdk/src/java.base/share/classes/java/lang/AbstractStringBuilder.java index 2aa9ff36a7f..f9c8eea6c89 100644 --- a/jdk/src/java.base/share/classes/java/lang/AbstractStringBuilder.java +++ b/jdk/src/java.base/share/classes/java/lang/AbstractStringBuilder.java @@ -1553,13 +1553,20 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { */ @Override public IntStream chars() { - byte[] val = this.value; int count = this.count; byte coder = this.coder; - checkOffset(count, val.length >> coder); // Reuse String-based spliterator. This requires a supplier to // capture the value and count when the terminal operation is executed return StreamSupport.intStream( - () -> coder == LATIN1 ? new StringLatin1.CharsSpliterator(val, 0, count, 0) - : new StringUTF16.CharsSpliterator(val, 0, count, 0), + () -> { + // The combined set of field reads are not atomic and thread + // safe but bounds checks will ensure no unsafe reads from + // the byte array + byte[] val = this.value; + int count = this.count; + byte coder = this.coder; + return coder == LATIN1 + ? new StringLatin1.CharsSpliterator(val, 0, count, 0) + : new StringUTF16.CharsSpliterator(val, 0, count, 0); + }, Spliterator.ORDERED | Spliterator.SIZED | Spliterator.SUBSIZED, false); } @@ -1570,13 +1577,20 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { */ @Override public IntStream codePoints() { - byte[] val = this.value; int count = this.count; byte coder = this.coder; - checkOffset(count, val.length >> coder); // Reuse String-based spliterator. This requires a supplier to // capture the value and count when the terminal operation is executed return StreamSupport.intStream( - () -> coder == LATIN1 ? new StringLatin1.CharsSpliterator(val, 0, count, 0) - : new StringUTF16.CodePointsSpliterator(val, 0, count, 0), + () -> { + // The combined set of field reads are not atomic and thread + // safe but bounds checks will ensure no unsafe reads from + // the byte array + byte[] val = this.value; + int count = this.count; + byte coder = this.coder; + return coder == LATIN1 + ? new StringLatin1.CharsSpliterator(val, 0, count, 0) + : new StringUTF16.CodePointsSpliterator(val, 0, count, 0); + }, Spliterator.ORDERED, false); } diff --git a/jdk/src/java.base/share/classes/java/lang/StringUTF16.java b/jdk/src/java.base/share/classes/java/lang/StringUTF16.java index 600bdd45154..2c0dd917a8f 100644 --- a/jdk/src/java.base/share/classes/java/lang/StringUTF16.java +++ b/jdk/src/java.base/share/classes/java/lang/StringUTF16.java @@ -811,7 +811,9 @@ final class StringUTF16 { throw new NullPointerException(); if (((a = array).length >> 1) >= (hi = fence) && (i = index) >= 0 && i < (index = hi)) { - do { action.accept(getChar(a, i)); } while (++i < hi); + do { + action.accept(charAt(a, i)); + } while (++i < hi); } } @@ -819,8 +821,10 @@ final class StringUTF16 { public boolean tryAdvance(IntConsumer action) { if (action == null) throw new NullPointerException(); - if (index >= 0 && index < fence) { - action.accept(getChar(array, index++)); + int i = index; + if (i >= 0 && i < fence) { + action.accept(charAt(array, i)); + index++; return true; } return false; @@ -860,8 +864,8 @@ final class StringUTF16 { int midOneLess; // If the mid-point intersects a surrogate pair - if (Character.isLowSurrogate(getChar(array, mid)) && - Character.isHighSurrogate(getChar(array, midOneLess = (mid -1)))) { + if (Character.isLowSurrogate(charAt(array, mid)) && + Character.isHighSurrogate(charAt(array, midOneLess = (mid -1)))) { // If there is only one pair it cannot be split if (lo >= midOneLess) return null; @@ -898,10 +902,10 @@ final class StringUTF16 { // Advance one code point from the index, i, and return the next // index to advance from private static int advance(byte[] a, int i, int hi, IntConsumer action) { - char c1 = getChar(a, i++); + char c1 = charAt(a, i++); int cp = c1; if (Character.isHighSurrogate(c1) && i < hi) { - char c2 = getChar(a, i); + char c2 = charAt(a, i); if (Character.isLowSurrogate(c2)) { i++; cp = Character.toCodePoint(c1, c2); diff --git a/jdk/test/TEST.groups b/jdk/test/TEST.groups index def84c827b3..448f871ee4d 100644 --- a/jdk/test/TEST.groups +++ b/jdk/test/TEST.groups @@ -741,7 +741,8 @@ needs_compact2 = \ java/util/ResourceBundle/Bug6359330.java \ java/util/Spliterator/SpliteratorCharacteristics.java \ java/util/Spliterator/SpliteratorCollisions.java \ - java/util/Spliterator/SpliteratorLateBindingFailFastTest.java \ + java/util/Spliterator/SpliteratorLateBindingTest.java \ + java/util/Spliterator/SpliteratorFailFastTest.java \ java/util/Spliterator/SpliteratorTraversingAndSplittingTest.java \ java/util/StringJoiner/MergeTest.java \ java/util/StringJoiner/StringJoinerTest.java \ diff --git a/jdk/test/java/util/Spliterator/SpliteratorFailFastTest.java b/jdk/test/java/util/Spliterator/SpliteratorFailFastTest.java new file mode 100644 index 00000000000..a0e71d24921 --- /dev/null +++ b/jdk/test/java/util/Spliterator/SpliteratorFailFastTest.java @@ -0,0 +1,201 @@ +/* + * Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.ConcurrentModificationException; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.PriorityQueue; +import java.util.Spliterator; +import java.util.Stack; +import java.util.TreeMap; +import java.util.TreeSet; +import java.util.Vector; +import java.util.WeakHashMap; +import java.util.function.Supplier; + +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertTrue; + +/** + * @test + * @bug 8148748 + * @summary Spliterator fail-fast tests + * @run testng SpliteratorFailFastTest + */ + +@Test +public class SpliteratorFailFastTest extends SpliteratorLateBindingFailFastHelper { + + static Object[][] spliteratorDataProvider; + + @DataProvider(name = "Source") + public static Object[][] spliteratorDataProvider() { + if (spliteratorDataProvider != null) { + return spliteratorDataProvider; + } + + List data = new ArrayList<>(); + SpliteratorDataBuilder db = + new SpliteratorDataBuilder<>(data, 5, Arrays.asList(1, 2, 3, 4)); + + // Collections + + db.addList(ArrayList::new); + + db.addList(LinkedList::new); + + db.addList(Vector::new); + + db.addList(AbstractRandomAccessListImpl::new); + + db.addCollection(HashSet::new); + + db.addCollection(LinkedHashSet::new); + + db.addCollection(TreeSet::new); + + db.addCollection(c -> { + Stack s = new Stack<>(); + s.addAll(c); + return s; + }); + + db.addCollection(PriorityQueue::new); + + // ArrayDeque fails some tests since its fail-fast support is weaker + // than other collections and limited to detecting most, but not all, + // removals. It probably requires its own test since it is difficult + // to abstract out the conditions under which it fails-fast. +// db.addCollection(ArrayDeque::new); + + // Maps + + db.addMap(HashMap::new); + + db.addMap(LinkedHashMap::new); + + // This fails when run through jtreg but passes when run through + // ant +// db.addMap(IdentityHashMap::new); + + db.addMap(WeakHashMap::new); + + // @@@ Descending maps etc + db.addMap(TreeMap::new); + + return spliteratorDataProvider = data.toArray(new Object[0][]); + } + + @Test(dataProvider = "Source") + public void testTryAdvance(String description, Supplier> ss) { + { + Source source = ss.get(); + Spliterator s = source.spliterator(); + + s.tryAdvance(e -> { + }); + source.update(); + + executeAndCatch(() -> s.tryAdvance(e -> { + })); + } + + { + Source source = ss.get(); + Spliterator s = source.spliterator(); + + s.tryAdvance(e -> { + }); + source.update(); + + executeAndCatch(() -> s.forEachRemaining(e -> { + })); + } + } + + @Test(dataProvider = "Source") + public void testForEach(String description, Supplier> ss) { + Source source = ss.get(); + Spliterator s = source.spliterator(); + + executeAndCatch(() -> s.forEachRemaining(e -> { + source.update(); + })); + } + + @Test(dataProvider = "Source") + public void testEstimateSize(String description, Supplier> ss) { + { + Source source = ss.get(); + Spliterator s = source.spliterator(); + + s.estimateSize(); + source.update(); + + executeAndCatch(() -> s.tryAdvance(e -> { + })); + } + + { + Source source = ss.get(); + Spliterator s = source.spliterator(); + + s.estimateSize(); + source.update(); + + executeAndCatch(() -> s.forEachRemaining(e -> { + })); + } + } + + private void executeAndCatch(Runnable r) { + executeAndCatch(ConcurrentModificationException.class, r); + } + + private void executeAndCatch(Class expected, Runnable r) { + Exception caught = null; + try { + r.run(); + } + catch (Exception e) { + caught = e; + } + + assertNotNull(caught, + String.format("No Exception was thrown, expected an Exception of %s to be thrown", + expected.getName())); + assertTrue(expected.isInstance(caught), + String.format("Exception thrown %s not an instance of %s", + caught.getClass().getName(), expected.getName())); + } + +} diff --git a/jdk/test/java/util/Spliterator/SpliteratorLateBindingFailFastHelper.java b/jdk/test/java/util/Spliterator/SpliteratorLateBindingFailFastHelper.java new file mode 100644 index 00000000000..a3c53b77b64 --- /dev/null +++ b/jdk/test/java/util/Spliterator/SpliteratorLateBindingFailFastHelper.java @@ -0,0 +1,224 @@ +/* + * Copyright (c) 2016 Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.util.AbstractList; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.RandomAccess; +import java.util.Spliterator; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Supplier; + +class SpliteratorLateBindingFailFastHelper { + + interface Source { + Spliterator spliterator(); + + void update(); + + default boolean bindOnCharacteristics() { + return false; + } + } + + static class IntSource implements Source { + final T b; + final Function toSpliterator; + final Consumer updater; + final boolean bindOnCharacteristics; + + public IntSource(T b, Function toSpliterator, + Consumer updater) { + this(b, toSpliterator, updater, false); + } + + public IntSource(T b, Function toSpliterator, + Consumer updater, boolean bindOnCharacteristics) { + this.b = b; + this.toSpliterator = toSpliterator; + this.updater = updater; + this.bindOnCharacteristics = bindOnCharacteristics; + } + + @Override + public Spliterator.OfInt spliterator() { + return toSpliterator.apply(b); + } + + @Override + public void update() { + updater.accept(b); + } + + @Override + public boolean bindOnCharacteristics() { + return bindOnCharacteristics; + } + } + + static class SpliteratorDataBuilder { + final List data; + + final T newValue; + + final List exp; + + final Map mExp; + + SpliteratorDataBuilder(List data, T newValue, List exp) { + this.data = data; + this.newValue = newValue; + this.exp = exp; + this.mExp = createMap(exp); + } + + Map createMap(List l) { + Map m = new LinkedHashMap<>(); + for (T t : l) { + m.put(t, t); + } + return m; + } + + void add(String description, Supplier> s) { + data.add(new Object[]{description, s}); + } + + void addCollection(Function, ? extends Collection> f) { + class CollectionSource implements Source { + final Collection c = f.apply(exp); + + final Consumer> updater; + + CollectionSource(Consumer> updater) { + this.updater = updater; + } + + @Override + public Spliterator spliterator() { + return c.spliterator(); + } + + @Override + public void update() { + updater.accept(c); + } + } + + String description = "new " + f.apply(Collections.emptyList()).getClass().getName() + ".spliterator() "; + add(description + "ADD", () -> new CollectionSource(c -> c.add(newValue))); + add(description + "REMOVE", () -> new CollectionSource(c -> c.remove(c.iterator().next()))); + } + + void addList(Function, ? extends List> l) { + addCollection(l); + addCollection(l.andThen(list -> list.subList(0, list.size()))); + } + + void addMap(Function, ? extends Map> mapConstructor) { + class MapSource implements Source { + final Map m = mapConstructor.apply(mExp); + + final Collection c; + + final Consumer> updater; + + MapSource(Function, Collection> f, Consumer> updater) { + this.c = f.apply(m); + this.updater = updater; + } + + @Override + public Spliterator spliterator() { + return c.spliterator(); + } + + @Override + public void update() { + updater.accept(m); + } + } + + Map>> actions = new HashMap<>(); + actions.put("ADD", m -> m.put(newValue, newValue)); + actions.put("REMOVE", m -> m.remove(m.keySet().iterator().next())); + + String description = "new " + mapConstructor.apply(Collections.emptyMap()).getClass().getName(); + for (Map.Entry>> e : actions.entrySet()) { + add(description + ".keySet().spliterator() " + e.getKey(), + () -> new MapSource<>(m -> m.keySet(), e.getValue())); + add(description + ".values().spliterator() " + e.getKey(), + () -> new MapSource<>(m -> m.values(), e.getValue())); + add(description + ".entrySet().spliterator() " + e.getKey(), + () -> new MapSource<>(m -> m.entrySet(), e.getValue())); + } + } + } + + static class AbstractRandomAccessListImpl extends AbstractList implements RandomAccess { + List l; + + AbstractRandomAccessListImpl(Collection c) { + this.l = new ArrayList<>(c); + } + + @Override + public boolean add(Integer integer) { + modCount++; + return l.add(integer); + } + + @Override + public Iterator iterator() { + return l.iterator(); + } + + @Override + public Integer get(int index) { + return l.get(index); + } + + @Override + public boolean remove(Object o) { + modCount++; + return l.remove(o); + } + + @Override + public int size() { + return l.size(); + } + + @Override + public List subList(int fromIndex, int toIndex) { + return l.subList(fromIndex, toIndex); + } + } +} diff --git a/jdk/test/java/util/Spliterator/SpliteratorLateBindingFailFastTest.java b/jdk/test/java/util/Spliterator/SpliteratorLateBindingFailFastTest.java deleted file mode 100644 index 0aecca604bd..00000000000 --- a/jdk/test/java/util/Spliterator/SpliteratorLateBindingFailFastTest.java +++ /dev/null @@ -1,401 +0,0 @@ -/* - * Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; - -import java.util.AbstractList; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.ConcurrentModificationException; -import java.util.Iterator; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedHashMap; -import java.util.LinkedHashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.PriorityQueue; -import java.util.RandomAccess; -import java.util.Set; -import java.util.Spliterator; -import java.util.Stack; -import java.util.TreeMap; -import java.util.TreeSet; -import java.util.Vector; -import java.util.WeakHashMap; -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.function.Supplier; - -import static org.testng.Assert.*; - -/** - * @test - * @bug 8148748 - * @summary Spliterator last-binding and fail-fast tests - * @run testng SpliteratorLateBindingFailFastTest - */ - -@Test -public class SpliteratorLateBindingFailFastTest { - - private interface Source { - Collection asCollection(); - void update(); - } - - private static class SpliteratorDataBuilder { - final List data; - - final T newValue; - - final List exp; - - final Map mExp; - - SpliteratorDataBuilder(List data, T newValue, List exp) { - this.data = data; - this.newValue = newValue; - this.exp = exp; - this.mExp = createMap(exp); - } - - Map createMap(List l) { - Map m = new LinkedHashMap<>(); - for (T t : l) { - m.put(t, t); - } - return m; - } - - void add(String description, Supplier> s) { - description = joiner(description).toString(); - data.add(new Object[]{description, s}); - } - - void addCollection(Function, ? extends Collection> f) { - class CollectionSource implements Source { - final Collection c = f.apply(exp); - - final Consumer> updater; - - CollectionSource(Consumer> updater) { - this.updater = updater; - } - - @Override - public Collection asCollection() { - return c; - } - - @Override - public void update() { - updater.accept(c); - } - } - - String description = "new " + f.apply(Collections.emptyList()).getClass().getName() + ".spliterator() "; - add(description + "ADD", () -> new CollectionSource(c -> c.add(newValue))); - add(description + "REMOVE", () -> new CollectionSource(c -> c.remove(c.iterator().next()))); - } - - void addList(Function, ? extends List> l) { - addCollection(l); - addCollection(l.andThen(list -> list.subList(0, list.size()))); - } - - void addMap(Function, ? extends Map> mapConstructor) { - class MapSource implements Source { - final Map m = mapConstructor.apply(mExp); - - final Collection c; - - final Consumer> updater; - - MapSource(Function, Collection> f, Consumer> updater) { - this.c = f.apply(m); - this.updater = updater; - } - - @Override - public Collection asCollection() { - return c; - } - - @Override - public void update() { - updater.accept(m); - } - } - - Map>> actions = new HashMap<>(); - actions.put("ADD", m -> m.put(newValue, newValue)); - actions.put("REMOVE", m -> m.remove(m.keySet().iterator().next())); - - String description = "new " + mapConstructor.apply(Collections.emptyMap()).getClass().getName(); - for (Map.Entry>> e : actions.entrySet()) { - add(description + ".keySet().spliterator() " + e.getKey(), - () -> new MapSource(m -> m.keySet(), e.getValue())); - add(description + ".values().spliterator() " + e.getKey(), - () -> new MapSource(m -> m.values(), e.getValue())); - add(description + ".entrySet().spliterator() " + e.getKey(), - () -> new MapSource>(m -> m.entrySet(), e.getValue())); - } - } - - StringBuilder joiner(String description) { - return new StringBuilder(description). - append(" {"). - append("size=").append(exp.size()). - append("}"); - } - } - - static Object[][] spliteratorDataProvider; - - @DataProvider(name = "Source") - public static Object[][] spliteratorDataProvider() { - if (spliteratorDataProvider != null) { - return spliteratorDataProvider; - } - - List data = new ArrayList<>(); - SpliteratorDataBuilder db = new SpliteratorDataBuilder<>(data, 5, Arrays.asList(1, 2, 3, 4)); - - // Collections - - db.addList(ArrayList::new); - - db.addList(LinkedList::new); - - db.addList(Vector::new); - - class AbstractRandomAccessListImpl extends AbstractList implements RandomAccess { - List l; - - AbstractRandomAccessListImpl(Collection c) { - this.l = new ArrayList<>(c); - } - - @Override - public boolean add(Integer integer) { - modCount++; - return l.add(integer); - } - - @Override - public Iterator iterator() { - return l.iterator(); - } - - @Override - public Integer get(int index) { - return l.get(index); - } - - @Override - public boolean remove(Object o) { - modCount++; - return l.remove(o); - } - - @Override - public int size() { - return l.size(); - } - - @Override - public List subList(int fromIndex, int toIndex) { - return l.subList(fromIndex, toIndex); - } - } - db.addList(AbstractRandomAccessListImpl::new); - - db.addCollection(HashSet::new); - - db.addCollection(LinkedHashSet::new); - - db.addCollection(TreeSet::new); - - - db.addCollection(c -> { Stack s = new Stack<>(); s.addAll(c); return s;}); - - db.addCollection(PriorityQueue::new); - - // ArrayDeque fails some tests since its fail-fast support is weaker - // than other collections and limited to detecting most, but not all, - // removals. It probably requires its own test since it is difficult - // to abstract out the conditions under which it fails-fast. -// db.addCollection(ArrayDeque::new); - - // Maps - - db.addMap(HashMap::new); - - db.addMap(LinkedHashMap::new); - - // This fails when run through jtreg but passes when run through - // ant -// db.addMap(IdentityHashMap::new); - - db.addMap(WeakHashMap::new); - - // @@@ Descending maps etc - db.addMap(TreeMap::new); - - return spliteratorDataProvider = data.toArray(new Object[0][]); - } - - @Test(dataProvider = "Source") - public void lateBindingTestWithForEach(String description, Supplier> ss) { - Source source = ss.get(); - Collection c = source.asCollection(); - Spliterator s = c.spliterator(); - - source.update(); - - Set r = new HashSet<>(); - s.forEachRemaining(r::add); - - assertEquals(r, new HashSet<>(c)); - } - - @Test(dataProvider = "Source") - public void lateBindingTestWithTryAdvance(String description, Supplier> ss) { - Source source = ss.get(); - Collection c = source.asCollection(); - Spliterator s = c.spliterator(); - - source.update(); - - Set r = new HashSet<>(); - while (s.tryAdvance(r::add)) { } - - assertEquals(r, new HashSet<>(c)); - } - - @Test(dataProvider = "Source") - public void lateBindingTestWithCharacteritics(String description, Supplier> ss) { - Source source = ss.get(); - Collection c = source.asCollection(); - Spliterator s = c.spliterator(); - s.characteristics(); - - Set r = new HashSet<>(); - s.forEachRemaining(r::add); - - assertEquals(r, new HashSet<>(c)); - } - - - @Test(dataProvider = "Source") - public void testFailFastTestWithTryAdvance(String description, Supplier> ss) { - { - Source source = ss.get(); - Collection c = source.asCollection(); - Spliterator s = c.spliterator(); - - s.tryAdvance(e -> { - }); - source.update(); - - executeAndCatch(() -> s.tryAdvance(e -> { })); - } - - { - Source source = ss.get(); - Collection c = source.asCollection(); - Spliterator s = c.spliterator(); - - s.tryAdvance(e -> { - }); - source.update(); - - executeAndCatch(() -> s.forEachRemaining(e -> { - })); - } - } - - @Test(dataProvider = "Source") - public void testFailFastTestWithForEach(String description, Supplier> ss) { - Source source = ss.get(); - Collection c = source.asCollection(); - Spliterator s = c.spliterator(); - - executeAndCatch(() -> s.forEachRemaining(e -> { - source.update(); - })); - } - - @Test(dataProvider = "Source") - public void testFailFastTestWithEstimateSize(String description, Supplier> ss) { - { - Source source = ss.get(); - Collection c = source.asCollection(); - Spliterator s = c.spliterator(); - - s.estimateSize(); - source.update(); - - executeAndCatch(() -> s.tryAdvance(e -> { })); - } - - { - Source source = ss.get(); - Collection c = source.asCollection(); - Spliterator s = c.spliterator(); - - s.estimateSize(); - source.update(); - - executeAndCatch(() -> s.forEachRemaining(e -> { - })); - } - } - - private void executeAndCatch(Runnable r) { - executeAndCatch(ConcurrentModificationException.class, r); - } - - private void executeAndCatch(Class expected, Runnable r) { - Exception caught = null; - try { - r.run(); - } - catch (Exception e) { - caught = e; - } - - assertNotNull(caught, - String.format("No Exception was thrown, expected an Exception of %s to be thrown", - expected.getName())); - assertTrue(expected.isInstance(caught), - String.format("Exception thrown %s not an instance of %s", - caught.getClass().getName(), expected.getName())); - } - -} diff --git a/jdk/test/java/util/Spliterator/SpliteratorLateBindingTest.java b/jdk/test/java/util/Spliterator/SpliteratorLateBindingTest.java new file mode 100644 index 00000000000..42553cbe4e5 --- /dev/null +++ b/jdk/test/java/util/Spliterator/SpliteratorLateBindingTest.java @@ -0,0 +1,221 @@ +/* + * Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import java.nio.CharBuffer; +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.BitSet; +import java.util.HashMap; +import java.util.HashSet; +import java.util.IdentityHashMap; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.PriorityQueue; +import java.util.Set; +import java.util.Spliterator; +import java.util.Stack; +import java.util.TreeMap; +import java.util.TreeSet; +import java.util.Vector; +import java.util.WeakHashMap; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.stream.Stream; + +import static org.testng.Assert.assertEquals; + +/** + * @test + * @bug 8148748 8170155 + * @summary Spliterator last-binding tests + * @run testng SpliteratorLateBindingTest + */ + +@Test +public class SpliteratorLateBindingTest extends SpliteratorLateBindingFailFastHelper { + + static Object[][] spliteratorDataProvider; + + @DataProvider(name = "Source") + public static Object[][] sourceDataProvider() { + if (spliteratorDataProvider != null) { + return spliteratorDataProvider; + } + + List data = new ArrayList<>(); + SpliteratorDataBuilder db = + new SpliteratorDataBuilder<>(data, 5, Arrays.asList(1, 2, 3, 4)); + + // Collections + + db.addList(ArrayList::new); + + db.addList(LinkedList::new); + + db.addList(Vector::new); + + db.addList(AbstractRandomAccessListImpl::new); + + db.addCollection(HashSet::new); + + db.addCollection(LinkedHashSet::new); + + db.addCollection(TreeSet::new); + + db.addCollection(c -> { + Stack s = new Stack<>(); + s.addAll(c); + return s; + }); + + db.addCollection(PriorityQueue::new); + + db.addCollection(ArrayDeque::new); + + // Maps + + db.addMap(HashMap::new); + + db.addMap(LinkedHashMap::new); + + db.addMap(IdentityHashMap::new); + + db.addMap(WeakHashMap::new); + + // @@@ Descending maps etc + db.addMap(TreeMap::new); + + // BitSet + + List bits = List.of(0, 1, 2); + Function bitsSource = bs -> bs.stream().spliterator(); + db.add("new BitSet.stream().spliterator() ADD", + () -> new IntSource<>(toBitSet(bits), bitsSource, bs -> bs.set(3))); + db.add("new BitSet.stream().spliterator() REMOVE", + () -> new IntSource<>(toBitSet(bits), bitsSource, bs -> bs.clear(2))); + + // CharSequence + + Function charsSource = sb -> sb.chars().spliterator(); + Function pointsSource = sb -> sb.codePoints().spliterator(); + + db.add("new StringBuilder.chars().spliterator() ADD", + () -> new IntSource<>(new StringBuilder("ABC"), charsSource, bs -> bs.append("D"), true)); + db.add("new StringBuilder.chars().spliterator() REMOVE", + () -> new IntSource<>(new StringBuilder("ABC"), charsSource, bs -> bs.deleteCharAt(2), true)); + db.add("new StringBuilder.codePoints().spliterator() ADD", + () -> new IntSource<>(new StringBuilder("ABC"), pointsSource, bs -> bs.append("D"), true)); + db.add("new StringBuilder.codePoints().spliterator() REMOVE", + () -> new IntSource<>(new StringBuilder("ABC"), pointsSource, bs -> bs.deleteCharAt(2), true)); + + db.add("new StringBuffer.chars().spliterator() ADD", + () -> new IntSource<>(new StringBuffer("ABC"), charsSource, bs -> bs.append("D"), true)); + db.add("new StringBuffer.chars().spliterator() REMOVE", + () -> new IntSource<>(new StringBuffer("ABC"), charsSource, bs -> bs.deleteCharAt(2), true)); + db.add("new StringBuffer.codePoints().spliterator() ADD", + () -> new IntSource<>(new StringBuffer("ABC"), pointsSource, bs -> bs.append("D"), true)); + db.add("new StringBuffer.codePoints().spliterator() REMOVE", + () -> new IntSource<>(new StringBuffer("ABC"), pointsSource, bs -> bs.deleteCharAt(2), true)); + + db.add("CharBuffer.wrap().chars().spliterator() ADD", + () -> new IntSource<>(CharBuffer.wrap("ABCD").limit(3), charsSource, bs -> bs.limit(4), true)); + db.add("CharBuffer.wrap().chars().spliterator() REMOVE", + () -> new IntSource<>(CharBuffer.wrap("ABCD"), charsSource, bs -> bs.limit(3), true)); + db.add("CharBuffer.wrap().codePoints().spliterator() ADD", + () -> new IntSource<>(CharBuffer.wrap("ABCD").limit(3), pointsSource, bs -> bs.limit(4), true)); + db.add("CharBuffer.wrap().codePoints().spliterator() REMOVE", + () -> new IntSource<>(CharBuffer.wrap("ABCD"), pointsSource, bs -> bs.limit(3), true)); + + return spliteratorDataProvider = data.toArray(new Object[0][]); + } + + + @DataProvider(name = "Source.Non.Binding.Characteristics") + public static Object[][] sourceCharacteristicsDataProvider() { + return Stream.of(sourceDataProvider()).filter(tc -> { + @SuppressWarnings("unchecked") + Supplier> s = (Supplier>) tc[1]; + return !s.get().bindOnCharacteristics(); + }).toArray(Object[][]::new); + } + + static BitSet toBitSet(List bits) { + BitSet bs = new BitSet(); + bits.forEach(bs::set); + return bs; + } + + + @Test(dataProvider = "Source") + public void testForEach(String description, Supplier> ss) { + Source source = ss.get(); + Spliterator s = source.spliterator(); + + source.update(); + + Set a = new HashSet<>(); + s.forEachRemaining(a::add); + + Set e = new HashSet<>(); + source.spliterator().forEachRemaining(e::add); + assertEquals(a, e); + } + + @Test(dataProvider = "Source") + public void testTryAdvance(String description, Supplier> ss) { + Source source = ss.get(); + Spliterator s = source.spliterator(); + + source.update(); + + Set a = new HashSet<>(); + while (s.tryAdvance(a::add)) { + } + + Set e = new HashSet<>(); + source.spliterator().forEachRemaining(e::add); + assertEquals(a, e); + } + + @Test(dataProvider = "Source.Non.Binding.Characteristics") + public void testCharacteristics(String description, Supplier> ss) { + Source source = ss.get(); + Spliterator s = source.spliterator(); + + s.characteristics(); + source.update(); + + Set a = new HashSet<>(); + s.forEachRemaining(a::add); + + Set e = new HashSet<>(); + source.spliterator().forEachRemaining(e::add); + assertEquals(a, e); + } +} diff --git a/jdk/test/java/util/Spliterator/SpliteratorTraversingAndSplittingTest.java b/jdk/test/java/util/Spliterator/SpliteratorTraversingAndSplittingTest.java index 474204384a8..a8338ca4e5f 100644 --- a/jdk/test/java/util/Spliterator/SpliteratorTraversingAndSplittingTest.java +++ b/jdk/test/java/util/Spliterator/SpliteratorTraversingAndSplittingTest.java @@ -31,6 +31,7 @@ import org.testng.annotations.DataProvider; import org.testng.annotations.Test; +import java.nio.CharBuffer; import java.util.AbstractCollection; import java.util.AbstractList; import java.util.AbstractSet; @@ -884,6 +885,7 @@ public class SpliteratorTraversingAndSplittingTest { cdb.add("new CharSequenceImpl(\"%s\")", CharSequenceImpl::new); cdb.add("new StringBuilder(\"%s\")", StringBuilder::new); cdb.add("new StringBuffer(\"%s\")", StringBuffer::new); + cdb.add("CharBuffer.wrap(\"%s\".toCharArray())", s -> CharBuffer.wrap(s.toCharArray())); }