--- old/src/share/classes/java/util/Collections.java 2013-08-26 20:48:26.089105157 -0700 +++ new/src/share/classes/java/util/Collections.java 2013-08-26 20:48:25.917105149 -0700 @@ -3879,6 +3879,7 @@ return batchRemove(c, true); } private boolean batchRemove(Collection c, boolean complement) { + Objects.requireNonNull(c); boolean modified = false; Iterator> it = iterator(); while (it.hasNext()) { --- old/src/share/classes/java/util/Hashtable.java 2013-08-26 20:48:26.821105193 -0700 +++ new/src/share/classes/java/util/Hashtable.java 2013-08-26 20:48:26.645105183 -0700 @@ -1020,6 +1020,8 @@ @Override public synchronized boolean replace(K key, V oldValue, V newValue) { + Objects.requireNonNull(oldValue); + Objects.requireNonNull(newValue); Entry tab[] = table; int hash = hash(key); int index = (hash & 0x7FFFFFFF) % tab.length; @@ -1040,6 +1042,7 @@ @Override public synchronized V replace(K key, V value) { + Objects.requireNonNull(value); Entry tab[] = table; int hash = hash(key); int index = (hash & 0x7FFFFFFF) % tab.length; --- old/src/share/classes/java/util/IdentityHashMap.java 2013-08-26 20:48:27.477105224 -0700 +++ new/src/share/classes/java/util/IdentityHashMap.java 2013-08-26 20:48:27.309105216 -0700 @@ -997,6 +997,7 @@ * behavior when c is a smaller "normal" (non-identity-based) Set. */ public boolean removeAll(Collection c) { + Objects.requireNonNull(c); boolean modified = false; for (Iterator i = iterator(); i.hasNext(); ) { if (c.contains(i.next())) { @@ -1212,6 +1213,7 @@ * behavior when c is a smaller "normal" (non-identity-based) Set. */ public boolean removeAll(Collection c) { + Objects.requireNonNull(c); boolean modified = false; for (Iterator> i = iterator(); i.hasNext(); ) { if (c.contains(i.next())) { --- old/src/share/classes/java/util/Map.java 2013-08-26 20:48:28.141105257 -0700 +++ new/src/share/classes/java/util/Map.java 2013-08-26 20:48:27.973105248 -0700 @@ -805,6 +805,10 @@ * return false; * } * + * The default implementation does not throw NullPointerException + * for maps that do not support null values if oldValue is null unless + * newValue is also null. + * * @param key key with which the specified value is associated * @param oldValue value expected to be associated with the specified key * @param newValue value to be associated with the specified key @@ -814,8 +818,11 @@ * (optional) * @throws ClassCastException if the class of a specified key or value * prevents it from being stored in this map - * @throws NullPointerException if a specified key or value is null, + * @throws NullPointerException if a specified key or newValue is null, * and this map does not permit null keys or values + * @throws NullPointerException if oldValue is null and this map does not + * permit null values + * (optional) * @throws IllegalArgumentException if some property of a specified key * or value prevents it from being stored in this map * @since 1.8 --- old/src/share/classes/java/util/TreeMap.java 2013-08-26 20:48:28.809105289 -0700 +++ new/src/share/classes/java/util/TreeMap.java 2013-08-26 20:48:28.633105280 -0700 @@ -1012,7 +1012,7 @@ int expectedModCount = modCount; for (Entry e = getFirstEntry(); e != null; e = successor(e)) { - e.value = Objects.requireNonNull(function.apply(e.key, e.value)); + e.value = function.apply(e.key, e.value); if (expectedModCount != modCount) { throw new ConcurrentModificationException(); --- old/src/share/classes/java/util/concurrent/ConcurrentHashMap.java 2013-08-26 20:48:29.505105323 -0700 +++ new/src/share/classes/java/util/concurrent/ConcurrentHashMap.java 2013-08-26 20:48:29.329105315 -0700 @@ -49,6 +49,7 @@ import java.util.Iterator; import java.util.Map; import java.util.NoSuchElementException; +import java.util.Objects; import java.util.Set; import java.util.Spliterator; import java.util.concurrent.ConcurrentMap; @@ -4410,6 +4411,7 @@ } public final boolean removeAll(Collection c) { + Objects.requireNonNull(c); boolean modified = false; for (Iterator it = iterator(); it.hasNext();) { if (c.contains(it.next())) { @@ -4421,6 +4423,7 @@ } public final boolean retainAll(Collection c) { + Objects.requireNonNull(c); boolean modified = false; for (Iterator it = iterator(); it.hasNext();) { if (!c.contains(it.next())) { --- old/src/share/classes/javax/security/auth/Subject.java 2013-08-26 20:48:30.249105359 -0700 +++ new/src/share/classes/javax/security/auth/Subject.java 2013-08-26 20:48:30.081105350 -0700 @@ -1186,7 +1186,7 @@ } public boolean removeAll(Collection c) { - + Objects.requireNonNull(c); boolean modified = false; final Iterator e = iterator(); while (e.hasNext()) { @@ -1222,7 +1222,7 @@ } public boolean retainAll(Collection c) { - + Objects.requireNonNull(c); boolean modified = false; boolean retain = false; final Iterator e = iterator(); --- old/test/java/util/Collection/CollectionDefaults.java 2013-08-26 20:48:30.925105393 -0700 +++ new/test/java/util/Collection/CollectionDefaults.java 2013-08-26 20:48:30.741105382 -0700 @@ -21,15 +21,19 @@ * questions. */ +import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; import java.util.Set; +import java.util.SortedSet; + import org.testng.annotations.DataProvider; import org.testng.annotations.Test; @@ -38,43 +42,68 @@ import java.util.TreeMap; import java.util.TreeSet; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentSkipListMap; import java.util.function.Predicate; +import java.util.function.Supplier; /** * @test + * @summary Unit tests for extension methods on Collection * @library testlibrary - * @build CollectionAsserts CollectionSupplier + * @build CollectionAsserts CollectionSupplier ExtendsAbstractSet ExtendsAbstractCollection * @run testng CollectionDefaults - * @summary Unit tests for extension methods on Collection */ public class CollectionDefaults { public static final Predicate pEven = x -> 0 == x % 2; public static final Predicate pOdd = x -> 1 == x % 2; - private static final String[] SET_CLASSES = { - "java.util.HashSet", - "java.util.LinkedHashSet", - "java.util.TreeSet" + @SuppressWarnings("unchecked") + private static final Supplier[] TEST_CLASSES = { + // Collection + ExtendsAbstractCollection::new, + + // Lists + java.util.ArrayList::new, + java.util.LinkedList::new, + java.util.Vector::new, + java.util.concurrent.CopyOnWriteArrayList::new, + ExtendsAbstractList::new, + + // Sets + java.util.HashSet::new, + java.util.LinkedHashSet::new, + java.util.TreeSet::new, + java.util.concurrent.ConcurrentSkipListSet::new, + java.util.concurrent.CopyOnWriteArraySet::new, + ExtendsAbstractSet::new }; private static final int SIZE = 100; @DataProvider(name="setProvider", parallel=true) - public static Object[][] setCases() { + public static Iterator setCases() { final List cases = new LinkedList<>(); cases.add(new Object[] { new HashSet<>() }); cases.add(new Object[] { new LinkedHashSet<>() }); cases.add(new Object[] { new TreeSet<>() }); + cases.add(new Object[] { new java.util.concurrent.ConcurrentSkipListSet<>() }); + cases.add(new Object[] { new java.util.concurrent.CopyOnWriteArraySet<>() }); + + cases.add(new Object[] { new ExtendsAbstractSet<>() }); cases.add(new Object[] { Collections.newSetFromMap(new HashMap<>()) }); cases.add(new Object[] { Collections.newSetFromMap(new LinkedHashMap()) }); cases.add(new Object[] { Collections.newSetFromMap(new TreeMap<>()) }); + cases.add(new Object[] { Collections.newSetFromMap(new ConcurrentHashMap<>()) }); + cases.add(new Object[] { Collections.newSetFromMap(new ConcurrentSkipListMap<>()) }); - cases.add(new Object[] { new HashSet(){{add(42);}} }); - cases.add(new Object[] { new LinkedHashSet(){{add(42);}} }); - cases.add(new Object[] { new TreeSet(){{add(42);}} }); - return cases.toArray(new Object[0][cases.size()]); + cases.add(new Object[] { new HashSet(){{add(42);}} }); + cases.add(new Object[] { new ExtendsAbstractSet(){{add(42);}} }); + cases.add(new Object[] { new LinkedHashSet(){{add(42);}} }); + cases.add(new Object[] { new TreeSet(){{add(42);}} }); + return cases.iterator(); } @Test(dataProvider = "setProvider") @@ -82,57 +111,66 @@ try { set.forEach(null); fail("expected NPE not thrown"); - } catch (NullPointerException npe) {} + } catch (NullPointerException expected) { + ; // expected + } try { set.removeIf(null); fail("expected NPE not thrown"); - } catch (NullPointerException npe) {} + } catch (NullPointerException expected) { + ; // expected + } } @Test public void testForEach() throws Exception { - final CollectionSupplier supplier = new CollectionSupplier(SET_CLASSES, SIZE); - for (final CollectionSupplier.TestCase test : supplier.get()) { - final Set original = ((Set) test.original); - final Set set = ((Set) test.collection); + final CollectionSupplier> supplier = new CollectionSupplier((Supplier>[]) TEST_CLASSES, SIZE); + + for (final CollectionSupplier.TestCase> test : supplier.get()) { + final Collection original = test.expected; + final Collection set = test.collection; try { set.forEach(null); fail("expected NPE not thrown"); - } catch (NullPointerException npe) {} - if (test.className.equals("java.util.HashSet")) { - CollectionAsserts.assertContentsUnordered(set, original); + } catch (NullPointerException expected) { + ; // expected + } + if (set instanceof Set && !((set instanceof SortedSet) || (set instanceof LinkedHashSet))) { + CollectionAsserts.assertContentsUnordered(set, original, test.toString()); } else { - CollectionAsserts.assertContents(set, original); + CollectionAsserts.assertContents(set, original, test.toString()); } final List actual = new LinkedList<>(); set.forEach(actual::add); - if (test.className.equals("java.util.HashSet")) { - CollectionAsserts.assertContentsUnordered(actual, set); - CollectionAsserts.assertContentsUnordered(actual, original); + if (set instanceof Set && !((set instanceof SortedSet) || (set instanceof LinkedHashSet))) { + CollectionAsserts.assertContentsUnordered(actual, set, test.toString()); + CollectionAsserts.assertContentsUnordered(actual, original, test.toString()); } else { - CollectionAsserts.assertContents(actual, set); - CollectionAsserts.assertContents(actual, original); + CollectionAsserts.assertContents(actual, set, test.toString()); + CollectionAsserts.assertContents(actual, original, test.toString()); } } } @Test public void testRemoveIf() throws Exception { - final CollectionSupplier supplier = new CollectionSupplier(SET_CLASSES, SIZE); - for (final CollectionSupplier.TestCase test : supplier.get()) { - final Set original = ((Set) test.original); - final Set set = ((Set) test.collection); + final CollectionSupplier> supplier = new CollectionSupplier((Supplier>[]) TEST_CLASSES, SIZE); + for (final CollectionSupplier.TestCase> test : supplier.get()) { + final Collection original = test.expected; + final Collection set = test.collection; try { set.removeIf(null); fail("expected NPE not thrown"); - } catch (NullPointerException npe) {} - if (test.className.equals("java.util.HashSet")) { - CollectionAsserts.assertContentsUnordered(set, original); + } catch (NullPointerException expected) { + ; // expected + } + if (set instanceof Set && !((set instanceof SortedSet) || (set instanceof LinkedHashSet))) { + CollectionAsserts.assertContentsUnordered(set, original, test.toString()); } else { - CollectionAsserts.assertContents(set, original); + CollectionAsserts.assertContents(set, original, test.toString()); } set.removeIf(pEven); --- old/test/java/util/Collection/MOAT.java 2013-08-26 20:48:31.573105424 -0700 +++ new/test/java/util/Collection/MOAT.java 2013-08-26 20:48:31.389105414 -0700 @@ -400,8 +400,6 @@ // If add(null) succeeds, contains(null) & remove(null) should succeed //---------------------------------------------------------------- private static void testNullElement(Collection c) { - // !!!! 5018849: (coll) TreeSet.contains(null) does not agree with Javadoc - if (c instanceof TreeSet) return; try { check(c.add(null)); --- old/test/java/util/Collection/testlibrary/CollectionAsserts.java 2013-08-26 20:48:32.233105455 -0700 +++ new/test/java/util/Collection/testlibrary/CollectionAsserts.java 2013-08-26 20:48:32.045105447 -0700 @@ -41,6 +41,10 @@ */ public class CollectionAsserts { + private CollectionAsserts() { + // no instances + } + public static void assertCountSum(Iterable it, int count, int sum) { assertCountSum(it.iterator(), count, sum); } @@ -117,10 +121,18 @@ } public static void assertContents(Iterable actual, Iterable expected) { - assertContents(actual.iterator(), expected.iterator()); + assertContents(actual, expected, null); + } + + public static void assertContents(Iterable actual, Iterable expected, String msg) { + assertContents(actual.iterator(), expected.iterator(), msg); } public static void assertContents(Iterator actual, Iterator expected) { + assertContents(actual, expected, null); + } + + public static void assertContents(Iterator actual, Iterator expected, String msg) { List history = new ArrayList<>(); while (expected.hasNext()) { @@ -128,20 +140,23 @@ List expectedData = new ArrayList<>(history); while (expected.hasNext()) expectedData.add(expected.next()); - fail(String.format("Premature end of data; expected=%s, found=%s", expectedData, history)); + fail(String.format("%s Premature end of data; expected=%s, found=%s", + (msg == null ? "" : msg), expectedData, history)); } T a = actual.next(); T e = expected.next(); history.add(a); if (!Objects.equals(a, e)) - fail(String.format("Data mismatch; preceding=%s, nextExpected=%s, nextFound=%s", history, e, a)); + fail(String.format("%s Data mismatch; preceding=%s, nextExpected=%s, nextFound=%s", + (msg == null ? "" : msg), history, e, a)); } if (actual.hasNext()) { List rest = new ArrayList<>(); while (actual.hasNext()) rest.add(actual.next()); - fail(String.format("Unexpected data %s after %s", rest, history)); + fail(String.format("%s Unexpected data %s after %s", + (msg == null ? "" : msg), rest, history)); } } @@ -151,30 +166,21 @@ assertContents(actual, Arrays.asList(expected).iterator()); } - public static boolean equalsContentsUnordered(Iterable a, Iterable b) { - Set sa = new HashSet<>(); - for (T t : a) { - sa.add(t); - } + public static> void assertContentsUnordered(Iterable actual, Iterable expected) { + assertContentsUnordered(actual, expected, null); + } - Set sb = new HashSet<>(); - for (T t : b) { - sb.add(t); + public static> void assertContentsUnordered(Iterable actual, Iterable expected, String msg) { + List allExpected = new ArrayList<>(); + for (T t : expected) { + allExpected.add(t); } - return Objects.equals(sa, sb); - } + for (T t : actual) { + assertTrue(allExpected.remove(t), msg + " element '" + String.valueOf(t) + "' not found"); + } - public static> void assertContentsUnordered(Iterable actual, Iterable expected) { - ArrayList one = new ArrayList<>(); - for (T t : actual) - one.add(t); - ArrayList two = new ArrayList<>(); - for (T t : expected) - two.add(t); - Collections.sort(one); - Collections.sort(two); - assertContents(one, two); + assertTrue(allExpected.isEmpty(), msg + "expected contained additional elements"); } static void assertSplitContents(Iterable> splits, Iterable list) { --- old/test/java/util/Collection/testlibrary/CollectionSupplier.java 2013-08-26 20:48:32.881105486 -0700 +++ new/test/java/util/Collection/testlibrary/CollectionSupplier.java 2013-08-26 20:48:32.697105477 -0700 @@ -29,13 +29,11 @@ import java.util.LinkedList; import java.util.List; import java.util.Random; -import java.util.Set; import org.testng.TestException; import static org.testng.Assert.assertTrue; -import java.lang.reflect.Constructor; import java.util.Collection; import java.util.Collections; import java.util.function.Supplier; @@ -44,15 +42,15 @@ * @library * @summary A Supplier of test cases for Collection tests */ -public final class CollectionSupplier implements Supplier> { +public final class CollectionSupplier> implements Supplier>> { - private final String[] classNames; + private final Supplier[] classes; private final int size; /** * A Collection test case. */ - public static final class TestCase { + public static final class TestCase> { /** * The name of the test case. @@ -60,57 +58,45 @@ public final String name; /** - * Class name of the instantiated Collection. - */ - public final String className; - - /** * Unmodifiable reference collection, useful for comparisons. */ - public final Collection original; + public final List expected; /** * A modifiable test collection. */ - public final Collection collection; + public final C collection; /** * Create a Collection test case. + * * @param name name of the test case - * @param className class name of the instantiated collection - * @param original reference collection + * @param expected reference collection * @param collection the modifiable test collection */ - public TestCase(String name, String className, - Collection original, Collection collection) { + public TestCase(String name, C collection) { this.name = name; - this.className = className; - this.original = - List.class.isAssignableFrom(original.getClass()) ? - Collections.unmodifiableList((List) original) : - Set.class.isAssignableFrom(original.getClass()) ? - Collections.unmodifiableSet((Set) original) : - Collections.unmodifiableCollection(original); + this.expected = Collections.unmodifiableList( + Arrays.asList(collection.toArray(new Integer[0]))); this.collection = collection; } @Override public String toString() { - return name + " " + className + - "\n original: " + original + - "\n target: " + collection; + return name + " " + collection.getClass().toString(); } } /** * Shuffle a list using a PRNG with known seed for repeatability + * * @param list the list to be shuffled */ public static void shuffle(final List list) { // PRNG with known seed for repeatable tests final Random prng = new Random(13); final int size = list.size(); - for (int i=0; i < size; i++) { + for (int i = 0; i < size; i++) { // random index in interval [i, size) final int j = i + prng.nextInt(size - i); // swap elements at indices i & j @@ -127,178 +113,133 @@ * @param classNames class names that implement {@code Collection} * @param size the desired size of each collection */ - public CollectionSupplier(String[] classNames, int size) { - this.classNames = Arrays.copyOf(classNames, classNames.length); + public CollectionSupplier(Supplier[] classes, int size) { + this.classes = Arrays.copyOf(classes, classes.length); this.size = size; } @Override - public Iterable get() { - try { - return getThrows(); - } catch (Exception e) { - throw new TestException(e); - } - } + public Iterable> get() { + final Collection> cases = new LinkedList<>(); + for (final Supplier type : classes) { + try { + final Collection empty = type.get(); + cases.add(new TestCase("empty", empty)); + + final Collection single = type.get(); + single.add(42); + cases.add(new TestCase("single", single)); + + final Collection regular = type.get(); + for (int i = 0; i < size; i++) { + regular.add(i); + } + cases.add(new TestCase("regular", regular)); - private Iterable getThrows() throws Exception { - final Collection collections = new LinkedList<>(); - for (final String className : classNames) { - @SuppressWarnings("unchecked") - final Class> type = - (Class>) Class.forName(className); - final Constructor> - defaultConstructor = type.getConstructor(); - final Constructor> - copyConstructor = type.getConstructor(Collection.class); - - final Collection empty = defaultConstructor.newInstance(); - collections.add(new TestCase("empty", - className, - copyConstructor.newInstance(empty), - empty)); - - final Collection single = defaultConstructor.newInstance(); - single.add(42); - collections.add(new TestCase("single", - className, - copyConstructor.newInstance(single), - single)); - - final Collection regular = defaultConstructor.newInstance(); - for (int i=0; i < size; i++) { - regular.add(i); - } - collections.add(new TestCase("regular", - className, - copyConstructor.newInstance(regular), - regular)); - - final Collection reverse = defaultConstructor.newInstance(); - for (int i=size; i >= 0; i--) { - reverse.add(i); - } - collections.add(new TestCase("reverse", - className, - copyConstructor.newInstance(reverse), - reverse)); - - final Collection odds = defaultConstructor.newInstance(); - for (int i=0; i < size; i++) { - odds.add((i * 2) + 1); - } - collections.add(new TestCase("odds", - className, - copyConstructor.newInstance(odds), - odds)); - - final Collection evens = defaultConstructor.newInstance(); - for (int i=0; i < size; i++) { - evens.add(i * 2); - } - collections.add(new TestCase("evens", - className, - copyConstructor.newInstance(evens), - evens)); - - final Collection fibonacci = defaultConstructor.newInstance(); - int prev2 = 0; - int prev1 = 1; - for (int i=0; i < size; i++) { - final int n = prev1 + prev2; - if (n < 0) { // stop on overflow - break; + final Collection reverse = type.get(); + for (int i = size; i >= 0; i--) { + reverse.add(i); } - fibonacci.add(n); - prev2 = prev1; - prev1 = n; - } - collections.add(new TestCase("fibonacci", - className, - copyConstructor.newInstance(fibonacci), - fibonacci)); + cases.add(new TestCase("reverse", reverse)); - // variants where the size of the backing storage != reported size - // created by removing half of the elements + final Collection odds = type.get(); + for (int i = 0; i < size; i++) { + odds.add((i * 2) + 1); + } + cases.add(new TestCase("odds", odds)); - final Collection emptyWithSlack = defaultConstructor.newInstance(); - emptyWithSlack.add(42); - assertTrue(emptyWithSlack.remove(42)); - collections.add(new TestCase("emptyWithSlack", - className, - copyConstructor.newInstance(emptyWithSlack), - emptyWithSlack)); - - final Collection singleWithSlack = defaultConstructor.newInstance(); - singleWithSlack.add(42); - singleWithSlack.add(43); - assertTrue(singleWithSlack.remove(43)); - collections.add(new TestCase("singleWithSlack", - className, - copyConstructor.newInstance(singleWithSlack), - singleWithSlack)); - - final Collection regularWithSlack = defaultConstructor.newInstance(); - for (int i=0; i < (2 * size); i++) { - regularWithSlack.add(i); - } - assertTrue(regularWithSlack.removeIf((x) -> {return x >= size;})); - collections.add(new TestCase("regularWithSlack", - className, - copyConstructor.newInstance(regularWithSlack), - regularWithSlack)); - - final Collection reverseWithSlack = defaultConstructor.newInstance(); - for (int i=2 * size; i >= 0; i--) { - reverseWithSlack.add(i); - } - assertTrue(reverseWithSlack.removeIf((x) -> {return x < size;})); - collections.add(new TestCase("reverseWithSlack", - className, - copyConstructor.newInstance(reverseWithSlack), - reverseWithSlack)); - - final Collection oddsWithSlack = defaultConstructor.newInstance(); - for (int i = 0; i < 2 * size; i++) { - oddsWithSlack.add((i * 2) + 1); - } - assertTrue(oddsWithSlack.removeIf((x) -> {return x >= size;})); - collections.add(new TestCase("oddsWithSlack", - className, - copyConstructor.newInstance(oddsWithSlack), - oddsWithSlack)); - - final Collection evensWithSlack = defaultConstructor.newInstance(); - for (int i = 0; i < 2 * size; i++) { - evensWithSlack.add(i * 2); - } - assertTrue(evensWithSlack.removeIf((x) -> {return x >= size;})); - collections.add(new TestCase("evensWithSlack", - className, - copyConstructor.newInstance(evensWithSlack), - evensWithSlack)); - - final Collection fibonacciWithSlack = defaultConstructor.newInstance(); - prev2 = 0; - prev1 = 1; - for (int i=0; i < size; i++) { - final int n = prev1 + prev2; - if (n < 0) { // stop on overflow - break; + final Collection evens = type.get(); + for (int i = 0; i < size; i++) { + evens.add(i * 2); } - fibonacciWithSlack.add(n); - prev2 = prev1; - prev1 = n; - } - assertTrue(fibonacciWithSlack.removeIf((x) -> {return x < 20;})); - collections.add(new TestCase("fibonacciWithSlack", - className, - copyConstructor.newInstance(fibonacciWithSlack), - fibonacciWithSlack)); + cases.add(new TestCase("evens", evens)); + + final Collection fibonacci = type.get(); + int prev2 = 0; + int prev1 = 1; + for (int i = 0; i < size; i++) { + final int n = prev1 + prev2; + if (n < 0) { // stop on overflow + break; + } + fibonacci.add(n); + prev2 = prev1; + prev1 = n; + } + cases.add(new TestCase("fibonacci", fibonacci)); + // variants where the size of the backing storage != reported size + // created by removing half of the elements + final Collection emptyWithSlack = type.get(); + emptyWithSlack.add(42); + assertTrue(emptyWithSlack.remove(42)); + cases.add(new TestCase("emptyWithSlack", emptyWithSlack)); + + final Collection singleWithSlack = type.get(); + singleWithSlack.add(42); + singleWithSlack.add(43); + assertTrue(singleWithSlack.remove(43)); + cases.add(new TestCase("singleWithSlack", singleWithSlack)); + + final Collection regularWithSlack = type.get(); + for (int i = 0; i < (2 * size); i++) { + regularWithSlack.add(i); + } + assertTrue(regularWithSlack.removeIf((x) -> { + return x >= size; + })); + cases.add(new TestCase("regularWithSlack", regularWithSlack)); + + final Collection reverseWithSlack = type.get(); + for (int i = 2 * size; i >= 0; i--) { + reverseWithSlack.add(i); + } + assertTrue(reverseWithSlack.removeIf((x) -> { + return x < size; + })); + cases.add(new TestCase("reverseWithSlack", reverseWithSlack)); + + final Collection oddsWithSlack = type.get(); + for (int i = 0; i < 2 * size; i++) { + oddsWithSlack.add((i * 2) + 1); + } + assertTrue(oddsWithSlack.removeIf((x) -> { + return x >= size; + })); + cases.add(new TestCase("oddsWithSlack", oddsWithSlack)); + + final Collection evensWithSlack = type.get(); + for (int i = 0; i < 2 * size; i++) { + evensWithSlack.add(i * 2); + } + assertTrue(evensWithSlack.removeIf((x) -> { + return x >= size; + })); + cases.add(new TestCase("evensWithSlack", evensWithSlack)); + + final Collection fibonacciWithSlack = type.get(); + prev2 = 0; + prev1 = 1; + for (int i = 0; i < size; i++) { + final int n = prev1 + prev2; + if (n < 0) { // stop on overflow + break; + } + fibonacciWithSlack.add(n); + prev2 = prev1; + prev1 = n; + } + assertTrue(fibonacciWithSlack.removeIf((x) -> { + return x < 20; + })); + cases.add(new TestCase("fibonacciWithSlack", + fibonacciWithSlack)); + } catch (Exception failed) { + throw new TestException(failed); + } } - return collections; + return cases; } } --- old/test/java/util/Map/Defaults.java 2013-08-26 20:48:33.541105517 -0700 +++ new/test/java/util/Map/Defaults.java 2013-08-26 20:48:33.353105511 -0700 @@ -155,7 +155,7 @@ assertThrows( () -> { map.replaceAll((k,v) -> null); }, NullPointerException.class, - description); + description + " should not allow replacement with null value"); } @Test(dataProvider = "Map rw=true keys=withNull values=withNull") @@ -194,6 +194,15 @@ assertSame(map.get(null), EXTRA_VALUE); } + @Test(dataProvider = "Map rw=true keys=nonNull values=nonNull") + public void testReplaceKVNoNulls(String description, Map map) { + assertTrue(map.containsKey(FIRST_KEY), "expected key missing"); + assertSame(map.get(FIRST_KEY), FIRST_VALUE, "found wrong value"); + assertThrows( () -> {map.replace(FIRST_KEY, null);}, NullPointerException.class, description + ": should throw NPE"); + assertSame(map.replace(FIRST_KEY, EXTRA_VALUE), FIRST_VALUE, description + ": replaced wrong value"); + assertSame(map.get(FIRST_KEY), EXTRA_VALUE, "found wrong value"); + } + @Test(dataProvider = "Map rw=true keys=all values=all") public void testReplaceKV(String description, Map map) { assertTrue(map.containsKey(KEYS[1])); @@ -224,6 +233,16 @@ assertSame(map.get(null), EXTRA_VALUE); } + @Test(dataProvider = "Map rw=true keys=nonNull values=nonNull") + public void testReplaceKVVNoNulls(String description, Map map) { + assertTrue(map.containsKey(FIRST_KEY), "expected key missing"); + assertSame(map.get(FIRST_KEY), FIRST_VALUE, "found wrong value"); + assertThrows( () -> {map.replace(FIRST_KEY, FIRST_VALUE, null);}, NullPointerException.class, description + ": should throw NPE"); + assertThrows( () -> {if (!map.replace(FIRST_KEY, null, EXTRA_VALUE)) throw new NullPointerException("default returns false rather than throwing");}, NullPointerException.class, description + ": should throw NPE"); + assertTrue(map.replace(FIRST_KEY, FIRST_VALUE, EXTRA_VALUE), description + ": replaced wrong value"); + assertSame(map.get(FIRST_KEY), EXTRA_VALUE, "found wrong value"); + } + @Test(dataProvider = "Map rw=true keys=all values=all") public void testReplaceKVV(String description, Map map) { assertTrue(map.containsKey(KEYS[1])); @@ -470,6 +489,9 @@ VALUES[each] = String.valueOf(each); } } + + private static final IntegerEnum FIRST_KEY = KEYS[0]; + private static final String FIRST_VALUE = VALUES[0]; private static final IntegerEnum EXTRA_KEY = IntegerEnum.EXTRA_KEY; private static final String EXTRA_VALUE = String.valueOf(TEST_SIZE); @@ -583,6 +605,8 @@ return Arrays.asList( // null key hostile new Object[]{"EnumMap", makeMap(() -> new EnumMap(IntegerEnum.class), false, nulls)}, + new Object[]{"TreeMap", makeMap(TreeMap::new, false, nulls)}, + new Object[]{"ExtendsAbstractMap(TreeMap)", makeMap(() -> {return new ExtendsAbstractMap(new TreeMap());}, false, nulls)}, new Object[]{"Collections.synchronizedMap(EnumMap)", Collections.synchronizedMap(makeMap(() -> new EnumMap(IntegerEnum.class), false, nulls))} ); } @@ -591,10 +615,11 @@ return Arrays.asList( // null key and value hostile new Object[]{"Hashtable", makeMap(Hashtable::new, false, false)}, - new Object[]{"TreeMap", makeMap(TreeMap::new, false, false)}, new Object[]{"ConcurrentHashMap", makeMap(ConcurrentHashMap::new, false, false)}, new Object[]{"ConcurrentSkipListMap", makeMap(ConcurrentSkipListMap::new, false, false)}, + new Object[]{"Collections.synchronizedMap(ConcurrentHashMap)", Collections.synchronizedMap(makeMap(ConcurrentHashMap::new, false, false))}, new Object[]{"Collections.checkedMap(ConcurrentHashMap)", Collections.checkedMap(makeMap(ConcurrentHashMap::new, false, false), IntegerEnum.class, String.class)}, + new Object[]{"ExtendsAbstractMap(ConcurrentHashMap)", makeMap(() -> {return new ExtendsAbstractMap(new ConcurrentHashMap());}, false, false)}, new Object[]{"ImplementsConcurrentMap", makeMap(ImplementsConcurrentMap::new, false, false)} ); } @@ -641,18 +666,17 @@ } public static void assertThrows(Thrower thrower, Class throwable, String message) { - Throwable result; + Throwable thrown; try { thrower.run(); - result = null; + thrown = null; } catch (Throwable caught) { - result = caught; + thrown = caught; } - assertInstance(result, throwable, - (null != message) - ? message - : "Failed to throw " + throwable.getCanonicalName()); + assertInstance(thrown, throwable, + ((null != message) ? message : "") + + " Failed to throw " + throwable.getCanonicalName()); } public static void assertThrows(Class throwable, String message, Thrower... throwers) { @@ -661,11 +685,11 @@ } } - public static void assertInstance(T actual, Class expected) { + public static void assertInstance(Object actual, Class expected) { assertInstance(expected.isInstance(actual), null); } - public static void assertInstance(T actual, Class expected, String message) { + public static void assertInstance(Object actual, Class expected, String message) { assertTrue(expected.isInstance(actual), message); } --- /dev/null 2013-08-23 12:27:01.471088020 -0700 +++ new/test/java/util/Collection/testlibrary/ExtendsAbstractCollection.java 2013-08-26 20:48:34.017105541 -0700 @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2012, 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.AbstractCollection; +import java.util.HashSet; +import java.util.AbstractSet; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.function.Supplier; + +/** + * @library + * + * A simple mutable collection implementation that provides only default + * implementations of all methods. ie. none of the Collection interface default + * methods have overridden implementations. + * + * @param type of collection elements + */ +public class ExtendsAbstractCollection extends AbstractCollection { + + protected final Collection coll; + + public ExtendsAbstractCollection() { + this(ArrayList::new); + } + + public ExtendsAbstractCollection(Collection source) { + this(); + coll.addAll(source); + } + + protected ExtendsAbstractCollection(Supplier> backer) { + this.coll = backer.get(); + } + + public boolean add(E element) { + return coll.add(element); + } + + public boolean remove(Object element) { + return coll.remove(element); + } + + public Iterator iterator() { + return new Iterator() { + Iterator source = coll.iterator(); + + public boolean hasNext() { + return source.hasNext(); + } + + public E next() { + return source.next(); + } + + public void remove() { + source.remove(); + } + }; + } + + public int size() { + return coll.size(); + } +} --- /dev/null 2013-08-23 12:27:01.471088020 -0700 +++ new/test/java/util/Collection/testlibrary/ExtendsAbstractList.java 2013-08-26 20:48:34.617105570 -0700 @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2012, 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.ArrayList; +import java.util.AbstractList; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.function.Supplier; + +/** + * @library + * + * A simple mutable list implementation that provides only default + * implementations of all methods. ie. none of the List interface default + * methods have overridden implementations. + * + * @param type of list elements + */ +public class ExtendsAbstractList extends AbstractList { + + protected final List list; + + public ExtendsAbstractList() { + this(ArrayList::new); + } + + protected ExtendsAbstractList(Supplier> supplier) { + this.list = supplier.get(); + } + + public ExtendsAbstractList(Collection source) { + this(); + addAll(source); + } + + public boolean add(E element) { + return list.add(element); + } + + public E get(int index) { + return list.get(index); + } + + public boolean remove(Object element) { + return list.remove(element); + } + + public E set(int index, E element) { + return list.set(index, element); + } + + public void add(int index, E element) { + list.add(index, element); + } + + public E remove(int index) { + return list.remove(index); + } + + public Iterator iterator() { + return new Iterator() { + Iterator source = list.iterator(); + + public boolean hasNext() { + return source.hasNext(); + } + + public E next() { + return source.next(); + } + + public void remove() { + source.remove(); + } + }; + } + + public int size() { + return list.size(); + } +} --- /dev/null 2013-08-23 12:27:01.471088020 -0700 +++ new/test/java/util/Collection/testlibrary/ExtendsAbstractSet.java 2013-08-26 20:48:35.225105599 -0700 @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2012, 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.HashSet; +import java.util.AbstractSet; +import java.util.Collection; +import java.util.Iterator; +import java.util.Set; +import java.util.function.Supplier; + +/** + * @library + * + * A simple mutable set implementation that provides only default + * implementations of all methods. ie. none of the Set interface default methods + * have overridden implementations. + * + * @param type of set members + */ +public class ExtendsAbstractSet extends AbstractSet { + + protected final Set set; + + public ExtendsAbstractSet() { + this(HashSet::new); + } + + public ExtendsAbstractSet(Collection source) { + this(); + addAll(source); + } + + protected ExtendsAbstractSet(Supplier> backer) { + this.set = backer.get(); + } + + public boolean add(E element) { + return set.add(element); + } + + public boolean remove(Object element) { + return set.remove(element); + } + + public Iterator iterator() { + return new Iterator() { + Iterator source = set.iterator(); + + public boolean hasNext() { + return source.hasNext(); + } + + public E next() { + return source.next(); + } + + public void remove() { + source.remove(); + } + }; + } + + public int size() { + return set.size(); + } +} --- old/test/java/util/Collection/ListDefaults.java 2013-08-26 20:48:36.025105638 -0700 +++ /dev/null 2013-08-23 12:27:01.471088020 -0700 @@ -1,536 +0,0 @@ -/* - * Copyright (c) 2012, 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.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; -import java.util.LinkedList; -import java.util.Stack; -import java.util.TreeMap; -import java.util.TreeSet; -import java.util.Vector; -import java.util.concurrent.CopyOnWriteArrayList; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicInteger; - -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; - -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertFalse; -import static org.testng.Assert.assertTrue; -import static org.testng.Assert.fail; - -import java.lang.reflect.Constructor; -import java.util.ConcurrentModificationException; -import java.util.function.Predicate; - -/** - * @test - * @bug 8023367 - * @library testlibrary - * @build CollectionAsserts CollectionSupplier - * @run testng ListDefaults - * @summary Unit tests for extension methods on List - */ -public class ListDefaults { - - private static final String[] LIST_CLASSES = { - "java.util.ArrayList", - "java.util.LinkedList", - "java.util.Vector", - "java.util.concurrent.CopyOnWriteArrayList" - }; - - private static final String[] LIST_CME_CLASSES = { - "java.util.ArrayList", - "java.util.Vector" - }; - - private static final Predicate pEven = x -> 0 == x % 2; - private static final Predicate pOdd = x -> 1 == x % 2; - - private static final Comparator BIT_COUNT_COMPARATOR = - (x, y) -> Integer.bitCount(x) - Integer.bitCount(y); - - private static final Comparator ATOMIC_INTEGER_COMPARATOR = - (x, y) -> x.intValue() - y.intValue(); - - private static final int SIZE = 100; - private static final int SUBLIST_FROM = 20; - private static final int SUBLIST_TO = SIZE - 5; - private static final int SUBLIST_SIZE = SUBLIST_TO - SUBLIST_FROM; - - private static interface Callback { - void call(List list); - } - - // call the callback for each recursive subList - private void trimmedSubList(final List list, final Callback callback) { - int size = list.size(); - if (size > 1) { - // trim 1 element from both ends - final List subList = list.subList(1, size - 1); - callback.call(subList); - trimmedSubList(subList, callback); - } - } - - @DataProvider(name="listProvider", parallel=true) - public static Object[][] listCases() { - final List cases = new LinkedList<>(); - cases.add(new Object[] { Collections.emptyList() }); - cases.add(new Object[] { new ArrayList<>() }); - cases.add(new Object[] { new LinkedList<>() }); - cases.add(new Object[] { new Vector<>() }); - cases.add(new Object[] { new Stack<>() }); - cases.add(new Object[] { new CopyOnWriteArrayList<>() }); - - cases.add(new Object[] { new ArrayList(){{add(42);}} }); - cases.add(new Object[] { new LinkedList(){{add(42);}} }); - cases.add(new Object[] { new Vector(){{add(42);}} }); - cases.add(new Object[] { new Stack(){{add(42);}} }); - cases.add(new Object[] { new CopyOnWriteArrayList(){{add(42);}} }); - return cases.toArray(new Object[0][cases.size()]); - } - - @Test(dataProvider = "listProvider") - public void testProvidedWithNull(final List list) throws Exception { - try { - list.forEach(null); - fail("expected NPE not thrown"); - } catch (NullPointerException npe) {} - try { - list.replaceAll(null); - fail("expected NPE not thrown"); - } catch (NullPointerException npe) {} - try { - list.removeIf(null); - fail("expected NPE not thrown"); - } catch (NullPointerException npe) {} - try { - list.sort(null); - } catch (Throwable t) { - fail("Exception not expected: " + t); - } - } - - @Test - public void testForEach() throws Exception { - final CollectionSupplier supplier = new CollectionSupplier(LIST_CLASSES, SIZE); - for (final CollectionSupplier.TestCase test : supplier.get()) { - final List original = ((List) test.original); - final List list = ((List) test.collection); - } - for (final CollectionSupplier.TestCase test : supplier.get()) { - final List original = ((List) test.original); - final List list = ((List) test.collection); - - try { - list.forEach(null); - fail("expected NPE not thrown"); - } catch (NullPointerException npe) {} - CollectionAsserts.assertContents(list, original); - - final List actual = new LinkedList<>(); - list.forEach(actual::add); - CollectionAsserts.assertContents(actual, list); - CollectionAsserts.assertContents(actual, original); - - if (original.size() > SUBLIST_SIZE) { - final List subList = original.subList(SUBLIST_FROM, SUBLIST_TO); - final List actualSubList = new LinkedList<>(); - subList.forEach(actualSubList::add); - assertEquals(actualSubList.size(), SUBLIST_SIZE); - for (int i = 0; i < SUBLIST_SIZE; i++) { - assertEquals(actualSubList.get(i), original.get(i + SUBLIST_FROM)); - } - } - - trimmedSubList(list, new Callback() { - @Override - public void call(final List list) { - final List actual = new LinkedList<>(); - list.forEach(actual::add); - CollectionAsserts.assertContents(actual, list); - } - }); - } - } - - @Test - public void testRemoveIf() throws Exception { - final CollectionSupplier supplier = new CollectionSupplier(LIST_CLASSES, SIZE); - - for (final CollectionSupplier.TestCase test : supplier.get()) { - final List original = ((List) test.original); - final List list = ((List) test.collection); - - try { - list.removeIf(null); - fail("expected NPE not thrown"); - } catch (NullPointerException npe) {} - CollectionAsserts.assertContents(list, original); - - final AtomicInteger offset = new AtomicInteger(1); - while (list.size() > 0) { - removeFirst(original, list, offset); - } - } - - for (final CollectionSupplier.TestCase test : supplier.get()) { - final List original = ((List) test.original); - final List list = ((List) test.collection); - list.removeIf(pOdd); - for (int i : list) { - assertTrue((i % 2) == 0); - } - for (int i : original) { - if (i % 2 == 0) { - assertTrue(list.contains(i)); - } - } - list.removeIf(pEven); - assertTrue(list.isEmpty()); - } - - for (final CollectionSupplier.TestCase test : supplier.get()) { - final List original = ((List) test.original); - final List list = ((List) test.collection); - final List listCopy = new ArrayList<>(list); - if (original.size() > SUBLIST_SIZE) { - final List subList = list.subList(SUBLIST_FROM, SUBLIST_TO); - final List subListCopy = new ArrayList<>(subList); - listCopy.removeAll(subList); - subList.removeIf(pOdd); - for (int i : subList) { - assertTrue((i % 2) == 0); - } - for (int i : subListCopy) { - if (i % 2 == 0) { - assertTrue(subList.contains(i)); - } else { - assertFalse(subList.contains(i)); - } - } - subList.removeIf(pEven); - assertTrue(subList.isEmpty()); - // elements outside the view should remain - CollectionAsserts.assertContents(list, listCopy); - } - } - - for (final CollectionSupplier.TestCase test : supplier.get()) { - final List list = ((List) test.collection); - trimmedSubList(list, new Callback() { - @Override - public void call(final List list) { - final List copy = new ArrayList<>(list); - list.removeIf(pOdd); - for (int i : list) { - assertTrue((i % 2) == 0); - } - for (int i : copy) { - if (i % 2 == 0) { - assertTrue(list.contains(i)); - } else { - assertFalse(list.contains(i)); - } - } - } - }); - } - } - - // remove the first element - private void removeFirst(final List original, final List list, final AtomicInteger offset) { - final AtomicBoolean first = new AtomicBoolean(true); - list.removeIf(x -> first.getAndSet(false)); - CollectionAsserts.assertContents(original.subList(offset.getAndIncrement(), original.size()), list); - } - - @Test - public void testReplaceAll() throws Exception { - final int scale = 3; - final CollectionSupplier supplier = new CollectionSupplier(LIST_CLASSES, SIZE); - for (final CollectionSupplier.TestCase test : supplier.get()) { - final List original = ((List) test.original); - final List list = ((List) test.collection); - - try { - list.replaceAll(null); - fail("expected NPE not thrown"); - } catch (NullPointerException npe) {} - CollectionAsserts.assertContents(list, original); - - list.replaceAll(x -> scale * x); - for (int i=0; i < original.size(); i++) { - assertTrue(list.get(i) == (scale * original.get(i)), "mismatch at index " + i); - } - - if (original.size() > SUBLIST_SIZE) { - final List subList = list.subList(SUBLIST_FROM, SUBLIST_TO); - subList.replaceAll(x -> x + 1); - // verify elements in view [from, to) were replaced - for (int i = 0; i < SUBLIST_SIZE; i++) { - assertTrue(subList.get(i) == ((scale * original.get(i + SUBLIST_FROM)) + 1), - "mismatch at sublist index " + i); - } - // verify that elements [0, from) remain unmodified - for (int i = 0; i < SUBLIST_FROM; i++) { - assertTrue(list.get(i) == (scale * original.get(i)), - "mismatch at original index " + i); - } - // verify that elements [to, size) remain unmodified - for (int i = SUBLIST_TO; i < list.size(); i++) { - assertTrue(list.get(i) == (scale * original.get(i)), - "mismatch at original index " + i); - } - } - } - - for (final CollectionSupplier.TestCase test : supplier.get()) { - final List list = ((List) test.collection); - trimmedSubList(list, new Callback() { - @Override - public void call(final List list) { - final List copy = new ArrayList<>(list); - final int offset = 5; - list.replaceAll(x -> offset + x); - for (int i=0; i < copy.size(); i++) { - assertTrue(list.get(i) == (offset + copy.get(i)), "mismatch at index " + i); - } - } - }); - } - } - - @Test - public void testSort() throws Exception { - final CollectionSupplier supplier = new CollectionSupplier(LIST_CLASSES, SIZE); - for (final CollectionSupplier.TestCase test : supplier.get()) { - final List original = ((List) test.original); - final List list = ((List) test.collection); - CollectionSupplier.shuffle(list); - list.sort(Integer::compare); - CollectionAsserts.assertSorted(list, Integer::compare); - if (test.name.startsWith("reverse")) { - Collections.reverse(list); - } - CollectionAsserts.assertContents(list, original); - - CollectionSupplier.shuffle(list); - list.sort(null); - CollectionAsserts.assertSorted(list, Comparator.naturalOrder()); - if (test.name.startsWith("reverse")) { - Collections.reverse(list); - } - CollectionAsserts.assertContents(list, original); - - CollectionSupplier.shuffle(list); - list.sort(Comparator.naturalOrder()); - CollectionAsserts.assertSorted(list, Comparator.naturalOrder()); - if (test.name.startsWith("reverse")) { - Collections.reverse(list); - } - CollectionAsserts.assertContents(list, original); - - CollectionSupplier.shuffle(list); - list.sort(Comparator.reverseOrder()); - CollectionAsserts.assertSorted(list, Comparator.reverseOrder()); - if (!test.name.startsWith("reverse")) { - Collections.reverse(list); - } - CollectionAsserts.assertContents(list, original); - - CollectionSupplier.shuffle(list); - list.sort(BIT_COUNT_COMPARATOR); - CollectionAsserts.assertSorted(list, BIT_COUNT_COMPARATOR); - // check sort by verifying that bitCount increases and never drops - int minBitCount = 0; - int bitCount = 0; - for (final Integer i : list) { - bitCount = Integer.bitCount(i); - assertTrue(bitCount >= minBitCount); - minBitCount = bitCount; - } - - @SuppressWarnings("unchecked") - final Class> type = - (Class>) Class.forName(test.className); - final Constructor> defaultConstructor = type.getConstructor(); - final List incomparables = (List) defaultConstructor.newInstance(); - - for (int i=0; i < test.original.size(); i++) { - incomparables.add(new AtomicInteger(i)); - } - CollectionSupplier.shuffle(incomparables); - incomparables.sort(ATOMIC_INTEGER_COMPARATOR); - for (int i=0; i < test.original.size(); i++) { - assertEquals(i, incomparables.get(i).intValue()); - } - - if (original.size() > SUBLIST_SIZE) { - final List copy = new ArrayList<>(list); - final List subList = list.subList(SUBLIST_FROM, SUBLIST_TO); - CollectionSupplier.shuffle(subList); - subList.sort(Comparator.naturalOrder()); - CollectionAsserts.assertSorted(subList, Comparator.naturalOrder()); - // verify that elements [0, from) remain unmodified - for (int i = 0; i < SUBLIST_FROM; i++) { - assertTrue(list.get(i) == copy.get(i), - "mismatch at index " + i); - } - // verify that elements [to, size) remain unmodified - for (int i = SUBLIST_TO; i < list.size(); i++) { - assertTrue(list.get(i) == copy.get(i), - "mismatch at index " + i); - } - } - } - - for (final CollectionSupplier.TestCase test : supplier.get()) { - final List list = ((List) test.collection); - trimmedSubList(list, new Callback() { - @Override - public void call(final List list) { - final List copy = new ArrayList<>(list); - CollectionSupplier.shuffle(list); - list.sort(Comparator.naturalOrder()); - CollectionAsserts.assertSorted(list, Comparator.naturalOrder()); - } - }); - } - } - - @Test - public void testForEachThrowsCME() throws Exception { - final CollectionSupplier supplier = new CollectionSupplier(LIST_CME_CLASSES, SIZE); - for (final CollectionSupplier.TestCase test : supplier.get()) { - final List list = ((List) test.collection); - if (list.size() <= 1) { - continue; - } - boolean gotException = false; - try { - // bad predicate that modifies its list, should throw CME - list.forEach((x) -> {list.add(x);}); - } catch (ConcurrentModificationException cme) { - gotException = true; - } - if (!gotException) { - fail("expected CME was not thrown from " + test); - } - } - } - - @Test - public void testRemoveIfThrowsCME() throws Exception { - final CollectionSupplier supplier = new CollectionSupplier(LIST_CME_CLASSES, SIZE); - for (final CollectionSupplier.TestCase test : supplier.get()) { - final List list = ((List) test.collection); - if (list.size() <= 1) { - continue; - } - boolean gotException = false; - try { - // bad predicate that modifies its list, should throw CME - list.removeIf((x) -> {return list.add(x);}); - } catch (ConcurrentModificationException cme) { - gotException = true; - } - if (!gotException) { - fail("expected CME was not thrown from " + test); - } - } - } - - @Test - public void testReplaceAllThrowsCME() throws Exception { - final CollectionSupplier supplier = new CollectionSupplier(LIST_CME_CLASSES, SIZE); - for (final CollectionSupplier.TestCase test : supplier.get()) { - final List list = ((List) test.collection); - if (list.size() <= 1) { - continue; - } - boolean gotException = false; - try { - // bad predicate that modifies its list, should throw CME - list.replaceAll(x -> {int n = 3 * x; list.add(n); return n;}); - } catch (ConcurrentModificationException cme) { - gotException = true; - } - if (!gotException) { - fail("expected CME was not thrown from " + test); - } - } - } - - @Test - public void testSortThrowsCME() throws Exception { - final CollectionSupplier supplier = new CollectionSupplier(LIST_CME_CLASSES, SIZE); - for (final CollectionSupplier.TestCase test : supplier.get()) { - final List list = ((List) test.collection); - if (list.size() <= 1) { - continue; - } - boolean gotException = false; - try { - // bad predicate that modifies its list, should throw CME - list.sort((x, y) -> {list.add(x); return x - y;}); - } catch (ConcurrentModificationException cme) { - gotException = true; - } - if (!gotException) { - fail("expected CME was not thrown from " + test); - } - } - } - - private static final List SLICED_EXPECTED = Arrays.asList(0, 1, 2, 3, 5, 6, 7, 8, 9); - private static final List SLICED_EXPECTED2 = Arrays.asList(0, 1, 2, 5, 6, 7, 8, 9); - - @DataProvider(name="shortIntListProvider", parallel=true) - public static Object[][] intListCases() { - final Integer[] DATA = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; - final List cases = new LinkedList<>(); - cases.add(new Object[] { new ArrayList<>(Arrays.asList(DATA)) }); - cases.add(new Object[] { new LinkedList<>(Arrays.asList(DATA)) }); - cases.add(new Object[] { new Vector<>(Arrays.asList(DATA)) }); - cases.add(new Object[] { new CopyOnWriteArrayList<>(Arrays.asList(DATA)) }); - return cases.toArray(new Object[0][cases.size()]); - } - - @Test(dataProvider = "shortIntListProvider") - public void testRemoveIfFromSlice(final List list) throws Exception { - final List sublist = list.subList(3, 6); - assertTrue(sublist.removeIf(x -> x == 4)); - CollectionAsserts.assertContents(list, SLICED_EXPECTED); - - final List sublist2 = list.subList(2, 5); - assertTrue(sublist2.removeIf(x -> x == 3)); - CollectionAsserts.assertContents(list, SLICED_EXPECTED2); - } -} --- /dev/null 2013-08-23 12:27:01.471088020 -0700 +++ new/test/java/util/List/ListDefaults.java 2013-08-26 20:48:35.837105629 -0700 @@ -0,0 +1,535 @@ +/* + * Copyright (c) 2012, 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.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.LinkedList; +import java.util.Stack; +import java.util.Vector; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertTrue; +import static org.testng.Assert.fail; + +import java.lang.reflect.Constructor; +import java.util.ConcurrentModificationException; +import java.util.function.Predicate; +import java.util.function.Supplier; + +/** + * @test + * @summary Unit tests for extension methods on List + * @bug 8023367 + * @library ../Collection/testlibrary + * @build CollectionAsserts CollectionSupplier ExtendsAbstractList + * @run testng ListDefaults + */ +public class ListDefaults { + + private static final Supplier[] LIST_CLASSES = { + java.util.ArrayList::new, + java.util.LinkedList::new, + java.util.Vector::new, + java.util.concurrent.CopyOnWriteArrayList::new, + ExtendsAbstractList::new + }; + + private static final Supplier[] LIST_CME_CLASSES = { + java.util.ArrayList::new, + java.util.Vector::new + }; + + private static final Predicate pEven = x -> 0 == x % 2; + private static final Predicate pOdd = x -> 1 == x % 2; + + private static final Comparator BIT_COUNT_COMPARATOR = + (x, y) -> Integer.bitCount(x) - Integer.bitCount(y); + + private static final Comparator ATOMIC_INTEGER_COMPARATOR = + (x, y) -> x.intValue() - y.intValue(); + + private static final int SIZE = 100; + private static final int SUBLIST_FROM = 20; + private static final int SUBLIST_TO = SIZE - 5; + private static final int SUBLIST_SIZE = SUBLIST_TO - SUBLIST_FROM; + + private static interface Callback { + void call(List list); + } + + // call the callback for each recursive subList + private void trimmedSubList(final List list, final Callback callback) { + int size = list.size(); + if (size > 1) { + // trim 1 element from both ends + final List subList = list.subList(1, size - 1); + callback.call(subList); + trimmedSubList(subList, callback); + } + } + + @DataProvider(name="listProvider", parallel=true) + public static Object[][] listCases() { + final List cases = new LinkedList<>(); + cases.add(new Object[] { Collections.emptyList() }); + cases.add(new Object[] { new ArrayList<>() }); + cases.add(new Object[] { new LinkedList<>() }); + cases.add(new Object[] { new Vector<>() }); + cases.add(new Object[] { new Stack<>() }); + cases.add(new Object[] { new CopyOnWriteArrayList<>() }); + + cases.add(new Object[] { new ArrayList(){{add(42);}} }); + cases.add(new Object[] { new LinkedList(){{add(42);}} }); + cases.add(new Object[] { new Vector(){{add(42);}} }); + cases.add(new Object[] { new Stack(){{add(42);}} }); + cases.add(new Object[] { new CopyOnWriteArrayList(){{add(42);}} }); + return cases.toArray(new Object[0][cases.size()]); + } + + @Test(dataProvider = "listProvider") + public void testProvidedWithNull(final List list) throws Exception { + try { + list.forEach(null); + fail("expected NPE not thrown"); + } catch (NullPointerException npe) {} + try { + list.replaceAll(null); + fail("expected NPE not thrown"); + } catch (NullPointerException npe) {} + try { + list.removeIf(null); + fail("expected NPE not thrown"); + } catch (NullPointerException npe) {} + try { + list.sort(null); + } catch (Throwable t) { + fail("Exception not expected: " + t); + } + } + + @Test + public void testForEach() throws Exception { + final CollectionSupplier> supplier = new CollectionSupplier((Supplier>[])LIST_CLASSES, SIZE); + for (final CollectionSupplier.TestCase> test : supplier.get()) { + final List original = ((List) test.expected); + final List list = ((List) test.collection); + + try { + list.forEach(null); + fail("expected NPE not thrown"); + } catch (NullPointerException npe) {} + CollectionAsserts.assertContents(list, original); + + final List actual = new LinkedList<>(); + list.forEach(actual::add); + CollectionAsserts.assertContents(actual, list); + CollectionAsserts.assertContents(actual, original); + + if (original.size() > SUBLIST_SIZE) { + final List subList = original.subList(SUBLIST_FROM, SUBLIST_TO); + final List actualSubList = new LinkedList<>(); + subList.forEach(actualSubList::add); + assertEquals(actualSubList.size(), SUBLIST_SIZE); + for (int i = 0; i < SUBLIST_SIZE; i++) { + assertEquals(actualSubList.get(i), original.get(i + SUBLIST_FROM)); + } + } + + trimmedSubList(list, new Callback() { + @Override + public void call(final List list) { + final List actual = new LinkedList<>(); + list.forEach(actual::add); + CollectionAsserts.assertContents(actual, list); + } + }); + } + } + + @Test + public void testRemoveIf() throws Exception { + final CollectionSupplier> supplier = new CollectionSupplier((Supplier>[])LIST_CLASSES, SIZE); + for (final CollectionSupplier.TestCase> test : supplier.get()) { + final List original = ((List) test.expected); + final List list = ((List) test.collection); + + try { + list.removeIf(null); + fail("expected NPE not thrown"); + } catch (NullPointerException npe) {} + CollectionAsserts.assertContents(list, original); + + final AtomicInteger offset = new AtomicInteger(1); + while (list.size() > 0) { + removeFirst(original, list, offset); + } + } + + for (final CollectionSupplier.TestCase test : supplier.get()) { + final List original = ((List) test.expected); + final List list = ((List) test.collection); + list.removeIf(pOdd); + for (int i : list) { + assertTrue((i % 2) == 0); + } + for (int i : original) { + if (i % 2 == 0) { + assertTrue(list.contains(i)); + } + } + list.removeIf(pEven); + assertTrue(list.isEmpty()); + } + + for (final CollectionSupplier.TestCase test : supplier.get()) { + final List original = ((List) test.expected); + final List list = ((List) test.collection); + final List listCopy = new ArrayList<>(list); + if (original.size() > SUBLIST_SIZE) { + final List subList = list.subList(SUBLIST_FROM, SUBLIST_TO); + final List subListCopy = new ArrayList<>(subList); + listCopy.removeAll(subList); + subList.removeIf(pOdd); + for (int i : subList) { + assertTrue((i % 2) == 0); + } + for (int i : subListCopy) { + if (i % 2 == 0) { + assertTrue(subList.contains(i)); + } else { + assertFalse(subList.contains(i)); + } + } + subList.removeIf(pEven); + assertTrue(subList.isEmpty()); + // elements outside the view should remain + CollectionAsserts.assertContents(list, listCopy); + } + } + + for (final CollectionSupplier.TestCase test : supplier.get()) { + final List list = ((List) test.collection); + trimmedSubList(list, new Callback() { + @Override + public void call(final List list) { + final List copy = new ArrayList<>(list); + list.removeIf(pOdd); + for (int i : list) { + assertTrue((i % 2) == 0); + } + for (int i : copy) { + if (i % 2 == 0) { + assertTrue(list.contains(i)); + } else { + assertFalse(list.contains(i)); + } + } + } + }); + } + } + + // remove the first element + private void removeFirst(final List original, final List list, final AtomicInteger offset) { + final AtomicBoolean first = new AtomicBoolean(true); + list.removeIf(x -> first.getAndSet(false)); + CollectionAsserts.assertContents(original.subList(offset.getAndIncrement(), original.size()), list); + } + + @Test + public void testReplaceAll() throws Exception { + final int scale = 3; + final CollectionSupplier> supplier = new CollectionSupplier((Supplier>[])LIST_CLASSES, SIZE); + for (final CollectionSupplier.TestCase> test : supplier.get()) { + final List original = ((List) test.expected); + final List list = ((List) test.collection); + + try { + list.replaceAll(null); + fail("expected NPE not thrown"); + } catch (NullPointerException npe) {} + CollectionAsserts.assertContents(list, original); + + list.replaceAll(x -> scale * x); + for (int i=0; i < original.size(); i++) { + assertTrue(list.get(i) == (scale * original.get(i)), "mismatch at index " + i); + } + + if (original.size() > SUBLIST_SIZE) { + final List subList = list.subList(SUBLIST_FROM, SUBLIST_TO); + subList.replaceAll(x -> x + 1); + // verify elements in view [from, to) were replaced + for (int i = 0; i < SUBLIST_SIZE; i++) { + assertTrue(subList.get(i) == ((scale * original.get(i + SUBLIST_FROM)) + 1), + "mismatch at sublist index " + i); + } + // verify that elements [0, from) remain unmodified + for (int i = 0; i < SUBLIST_FROM; i++) { + assertTrue(list.get(i) == (scale * original.get(i)), + "mismatch at original index " + i); + } + // verify that elements [to, size) remain unmodified + for (int i = SUBLIST_TO; i < list.size(); i++) { + assertTrue(list.get(i) == (scale * original.get(i)), + "mismatch at original index " + i); + } + } + } + + for (final CollectionSupplier.TestCase test : supplier.get()) { + final List list = ((List) test.collection); + trimmedSubList(list, new Callback() { + @Override + public void call(final List list) { + final List copy = new ArrayList<>(list); + final int offset = 5; + list.replaceAll(x -> offset + x); + for (int i=0; i < copy.size(); i++) { + assertTrue(list.get(i) == (offset + copy.get(i)), "mismatch at index " + i); + } + } + }); + } + } + + @Test + public void testSort() throws Exception { + final CollectionSupplier> supplier = new CollectionSupplier((Supplier>[])LIST_CLASSES, SIZE); + for (final CollectionSupplier.TestCase> test : supplier.get()) { + final List original = ((List) test.expected); + final List list = ((List) test.collection); + CollectionSupplier.shuffle(list); + list.sort(Integer::compare); + CollectionAsserts.assertSorted(list, Integer::compare); + if (test.name.startsWith("reverse")) { + Collections.reverse(list); + } + CollectionAsserts.assertContents(list, original); + + CollectionSupplier.shuffle(list); + list.sort(null); + CollectionAsserts.assertSorted(list, Comparator.naturalOrder()); + if (test.name.startsWith("reverse")) { + Collections.reverse(list); + } + CollectionAsserts.assertContents(list, original); + + CollectionSupplier.shuffle(list); + list.sort(Comparator.naturalOrder()); + CollectionAsserts.assertSorted(list, Comparator.naturalOrder()); + if (test.name.startsWith("reverse")) { + Collections.reverse(list); + } + CollectionAsserts.assertContents(list, original); + + CollectionSupplier.shuffle(list); + list.sort(Comparator.reverseOrder()); + CollectionAsserts.assertSorted(list, Comparator.reverseOrder()); + if (!test.name.startsWith("reverse")) { + Collections.reverse(list); + } + CollectionAsserts.assertContents(list, original); + + CollectionSupplier.shuffle(list); + list.sort(BIT_COUNT_COMPARATOR); + CollectionAsserts.assertSorted(list, BIT_COUNT_COMPARATOR); + // check sort by verifying that bitCount increases and never drops + int minBitCount = 0; + int bitCount = 0; + for (final Integer i : list) { + bitCount = Integer.bitCount(i); + assertTrue(bitCount >= minBitCount); + minBitCount = bitCount; + } + + @SuppressWarnings("unchecked") + final Constructor> defaultConstructor = ((Class>)test.collection.getClass()).getConstructor(); + final List incomparables = (List) defaultConstructor.newInstance(); + + for (int i=0; i < test.expected.size(); i++) { + incomparables.add(new AtomicInteger(i)); + } + CollectionSupplier.shuffle(incomparables); + incomparables.sort(ATOMIC_INTEGER_COMPARATOR); + for (int i=0; i < test.expected.size(); i++) { + assertEquals(i, incomparables.get(i).intValue()); + } + + if (original.size() > SUBLIST_SIZE) { + final List copy = new ArrayList<>(list); + final List subList = list.subList(SUBLIST_FROM, SUBLIST_TO); + CollectionSupplier.shuffle(subList); + subList.sort(Comparator.naturalOrder()); + CollectionAsserts.assertSorted(subList, Comparator.naturalOrder()); + // verify that elements [0, from) remain unmodified + for (int i = 0; i < SUBLIST_FROM; i++) { + assertTrue(list.get(i) == copy.get(i), + "mismatch at index " + i); + } + // verify that elements [to, size) remain unmodified + for (int i = SUBLIST_TO; i < list.size(); i++) { + assertTrue(list.get(i) == copy.get(i), + "mismatch at index " + i); + } + } + } + + for (final CollectionSupplier.TestCase test : supplier.get()) { + final List list = ((List) test.collection); + trimmedSubList(list, new Callback() { + @Override + public void call(final List list) { + final List copy = new ArrayList<>(list); + CollectionSupplier.shuffle(list); + list.sort(Comparator.naturalOrder()); + CollectionAsserts.assertSorted(list, Comparator.naturalOrder()); + } + }); + } + } + + @Test + public void testForEachThrowsCME() throws Exception { + final CollectionSupplier> supplier = new CollectionSupplier((Supplier>[])LIST_CME_CLASSES, SIZE); + for (final CollectionSupplier.TestCase> test : supplier.get()) { + final List list = ((List) test.collection); + + if (list.size() <= 1) { + continue; + } + boolean gotException = false; + try { + // bad predicate that modifies its list, should throw CME + list.forEach((x) -> {list.add(x);}); + } catch (ConcurrentModificationException cme) { + gotException = true; + } + if (!gotException) { + fail("expected CME was not thrown from " + test); + } + } + } + + @Test + public void testRemoveIfThrowsCME() throws Exception { + final CollectionSupplier> supplier = new CollectionSupplier((Supplier>[])LIST_CME_CLASSES, SIZE); + for (final CollectionSupplier.TestCase> test : supplier.get()) { + final List original = ((List) test.expected); + final List list = ((List) test.collection); + + if (list.size() <= 1) { + continue; + } + boolean gotException = false; + try { + // bad predicate that modifies its list, should throw CME + list.removeIf((x) -> {return list.add(x);}); + } catch (ConcurrentModificationException cme) { + gotException = true; + } + if (!gotException) { + fail("expected CME was not thrown from " + test); + } + } + } + + @Test + public void testReplaceAllThrowsCME() throws Exception { + final CollectionSupplier> supplier = new CollectionSupplier((Supplier>[])LIST_CME_CLASSES, SIZE); + for (final CollectionSupplier.TestCase> test : supplier.get()) { + final List list = ((List) test.collection); + + if (list.size() <= 1) { + continue; + } + boolean gotException = false; + try { + // bad predicate that modifies its list, should throw CME + list.replaceAll(x -> {int n = 3 * x; list.add(n); return n;}); + } catch (ConcurrentModificationException cme) { + gotException = true; + } + if (!gotException) { + fail("expected CME was not thrown from " + test); + } + } + } + + @Test + public void testSortThrowsCME() throws Exception { + final CollectionSupplier> supplier = new CollectionSupplier((Supplier>[])LIST_CME_CLASSES, SIZE); + for (final CollectionSupplier.TestCase> test : supplier.get()) { + final List list = ((List) test.collection); + + if (list.size() <= 1) { + continue; + } + boolean gotException = false; + try { + // bad predicate that modifies its list, should throw CME + list.sort((x, y) -> {list.add(x); return x - y;}); + } catch (ConcurrentModificationException cme) { + gotException = true; + } + if (!gotException) { + fail("expected CME was not thrown from " + test); + } + } + } + + private static final List SLICED_EXPECTED = Arrays.asList(0, 1, 2, 3, 5, 6, 7, 8, 9); + private static final List SLICED_EXPECTED2 = Arrays.asList(0, 1, 2, 5, 6, 7, 8, 9); + + @DataProvider(name="shortIntListProvider", parallel=true) + public static Object[][] intListCases() { + final Integer[] DATA = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + final List cases = new LinkedList<>(); + cases.add(new Object[] { new ArrayList<>(Arrays.asList(DATA)) }); + cases.add(new Object[] { new LinkedList<>(Arrays.asList(DATA)) }); + cases.add(new Object[] { new Vector<>(Arrays.asList(DATA)) }); + cases.add(new Object[] { new CopyOnWriteArrayList<>(Arrays.asList(DATA)) }); + cases.add(new Object[] { new ExtendsAbstractList<>(Arrays.asList(DATA)) }); + return cases.toArray(new Object[0][cases.size()]); + } + + @Test(dataProvider = "shortIntListProvider") + public void testRemoveIfFromSlice(final List list) throws Exception { + final List sublist = list.subList(3, 6); + assertTrue(sublist.removeIf(x -> x == 4)); + CollectionAsserts.assertContents(list, SLICED_EXPECTED); + + final List sublist2 = list.subList(2, 5); + assertTrue(sublist2.removeIf(x -> x == 3)); + CollectionAsserts.assertContents(list, SLICED_EXPECTED2); + } +}