1 /*
   2  * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 import java.util.ArrayList;
  25 import java.util.Arrays;
  26 import java.util.Collections;
  27 import java.util.Comparator;
  28 import java.util.List;
  29 import java.util.LinkedList;
  30 import java.util.Stack;
  31 import java.util.TreeMap;
  32 import java.util.TreeSet;
  33 import java.util.Vector;
  34 import java.util.concurrent.CopyOnWriteArrayList;
  35 import java.util.concurrent.atomic.AtomicBoolean;
  36 import java.util.concurrent.atomic.AtomicInteger;
  37 
  38 import org.testng.annotations.DataProvider;
  39 import org.testng.annotations.Test;
  40 
  41 import static org.testng.Assert.assertEquals;
  42 import static org.testng.Assert.assertFalse;
  43 import static org.testng.Assert.assertTrue;
  44 import static org.testng.Assert.fail;
  45 
  46 import java.lang.reflect.Constructor;
  47 import java.util.Collection;
  48 import java.util.ConcurrentModificationException;
  49 import java.util.function.Predicate;
  50 
  51 /**
  52  * @test
  53  * @summary Unit tests for extension methods on List
  54  * @library ../Collection/testlibrary
  55  * @build CollectionAsserts CollectionSupplier ExtendsAbstractList
  56  * @run testng ListDefaults
  57  */
  58 public class ListDefaults {
  59 
  60     private static final String[] LIST_CLASSES = {
  61         "java.util.ArrayList",
  62         "java.util.LinkedList",
  63         "java.util.Vector",
  64         "java.util.concurrent.CopyOnWriteArrayList",
  65         "ExtendsAbstractList"
  66     };
  67 
  68     private static final String[] LIST_CME_CLASSES = {
  69         "java.util.ArrayList",
  70         "java.util.Vector"
  71     };
  72 
  73     private static final Predicate<Integer> pEven = x -> 0 == x % 2;
  74     private static final Predicate<Integer> pOdd = x -> 1 == x % 2;
  75 
  76     private static final Comparator<Integer> BIT_COUNT_COMPARATOR =
  77             (x, y) -> Integer.bitCount(x) - Integer.bitCount(y);
  78 
  79     private static final Comparator<AtomicInteger> ATOMIC_INTEGER_COMPARATOR =
  80             (x, y) -> x.intValue() - y.intValue();
  81 
  82     private static final int SIZE = 100;
  83     private static final int SUBLIST_FROM = 20;
  84     private static final int SUBLIST_TO = SIZE - 5;
  85     private static final int SUBLIST_SIZE = SUBLIST_TO - SUBLIST_FROM;
  86 
  87     private static interface Callback {
  88         void call(List<Integer> list);
  89     }
  90 
  91     // call the callback for each recursive subList
  92     private void trimmedSubList(final List<Integer> list, final Callback callback) {
  93         int size = list.size();
  94         if (size > 1) {
  95             // trim 1 element from both ends
  96             final List<Integer> subList = list.subList(1, size - 1);
  97             callback.call(subList);
  98             trimmedSubList(subList, callback);
  99         }
 100     }
 101 
 102     @DataProvider(name="listProvider", parallel=true)
 103     public static Object[][] listCases() {
 104         final List<Object[]> cases = new LinkedList<>();
 105         cases.add(new Object[] { new ArrayList<>() });
 106         cases.add(new Object[] { new LinkedList<>() });
 107         cases.add(new Object[] { new Vector<>() });
 108         cases.add(new Object[] { new Stack<>() });
 109         cases.add(new Object[] { new CopyOnWriteArrayList<>() });
 110 
 111         cases.add(new Object[] { new ArrayList(){{add(42);}} });
 112         cases.add(new Object[] { new LinkedList(){{add(42);}} });
 113         cases.add(new Object[] { new Vector(){{add(42);}} });
 114         cases.add(new Object[] { new Stack(){{add(42);}} });
 115         cases.add(new Object[] { new CopyOnWriteArrayList(){{add(42);}} });
 116         return cases.toArray(new Object[0][cases.size()]);
 117     }
 118 
 119     @Test(dataProvider = "listProvider")
 120     public void testProvidedWithNull(final List<Integer> list) throws Exception {
 121         try {
 122             list.forEach(null);
 123             fail("expected NPE not thrown");
 124         } catch (NullPointerException npe) {}
 125         try {
 126             list.replaceAll(null);
 127             fail("expected NPE not thrown");
 128         } catch (NullPointerException npe) {}
 129         try {
 130             list.removeIf(null);
 131             fail("expected NPE not thrown");
 132         } catch (NullPointerException npe) {}
 133     }
 134 
 135     @Test
 136     public void testForEach() throws Exception {
 137         final CollectionSupplier supplier = new CollectionSupplier(LIST_CLASSES, SIZE);
 138         for (final CollectionSupplier.TestCase test : supplier.get()) {
 139             final List<Integer> original = ((List<Integer>) test.original);
 140             final List<Integer> list = ((List<Integer>) test.collection);
 141         }
 142         for (final CollectionSupplier.TestCase test : supplier.get()) {
 143             final List<Integer> original = ((List<Integer>) test.original);
 144             final List<Integer> list = ((List<Integer>) test.collection);
 145 
 146             try {
 147                 list.forEach(null);
 148                 fail("expected NPE not thrown");
 149             } catch (NullPointerException npe) {}
 150             CollectionAsserts.assertContents(list, original);
 151 
 152             final List<Integer> actual = new LinkedList<>();
 153             list.forEach(actual::add);
 154             CollectionAsserts.assertContents(actual, list);
 155             CollectionAsserts.assertContents(actual, original);
 156 
 157             if (original.size() > SUBLIST_SIZE) {
 158                 final List<Integer> subList = original.subList(SUBLIST_FROM, SUBLIST_TO);
 159                 final List<Integer> actualSubList = new LinkedList<>();
 160                 subList.forEach(actualSubList::add);
 161                 assertEquals(actualSubList.size(), SUBLIST_SIZE);
 162                 for (int i = 0; i < SUBLIST_SIZE; i++) {
 163                     assertEquals(actualSubList.get(i), original.get(i + SUBLIST_FROM));
 164                 }
 165             }
 166 
 167             trimmedSubList(list, new Callback() {
 168                 @Override
 169                 public void call(final List<Integer> list) {
 170                     final List<Integer> actual = new LinkedList<>();
 171                     list.forEach(actual::add);
 172                     CollectionAsserts.assertContents(actual, list);
 173                 }
 174             });
 175         }
 176     }
 177 
 178     @Test
 179     public void testRemoveIf() throws Exception {
 180         final CollectionSupplier supplier = new CollectionSupplier(LIST_CLASSES, SIZE);
 181 
 182         for (final CollectionSupplier.TestCase test : supplier.get()) {
 183             final List<Integer> original = ((List<Integer>) test.original);
 184             final List<Integer> list = ((List<Integer>) test.collection);
 185 
 186             try {
 187                 list.removeIf(null);
 188                 fail("expected NPE not thrown");
 189             } catch (NullPointerException npe) {}
 190             CollectionAsserts.assertContents(list, original);
 191 
 192             final AtomicInteger offset = new AtomicInteger(1);
 193             while (list.size() > 0) {
 194                 removeFirst(original, list, offset);
 195             }
 196         }
 197 
 198         for (final CollectionSupplier.TestCase test : supplier.get()) {
 199             final List<Integer> original = ((List<Integer>) test.original);
 200             final List<Integer> list = ((List<Integer>) test.collection);
 201             list.removeIf(pOdd);
 202             for (int i : list) {
 203                 assertTrue((i % 2) == 0);
 204             }
 205             for (int i : original) {
 206                 if (i % 2 == 0) {
 207                     assertTrue(list.contains(i));
 208                 }
 209             }
 210             list.removeIf(pEven);
 211             assertTrue(list.isEmpty());
 212         }
 213 
 214         for (final CollectionSupplier.TestCase test : supplier.get()) {
 215             final List<Integer> original = ((List<Integer>) test.original);
 216             final List<Integer> list = ((List<Integer>) test.collection);
 217             final List<Integer> listCopy = new ArrayList<>(list);
 218             if (original.size() > SUBLIST_SIZE) {
 219                 final List<Integer> subList = list.subList(SUBLIST_FROM, SUBLIST_TO);
 220                 final List<Integer> subListCopy = new ArrayList<>(subList);
 221                 listCopy.removeAll(subList);
 222                 subList.removeIf(pOdd);
 223                 for (int i : subList) {
 224                     assertTrue((i % 2) == 0);
 225                 }
 226                 for (int i : subListCopy) {
 227                     if (i % 2 == 0) {
 228                         assertTrue(subList.contains(i));
 229                     } else {
 230                         assertFalse(subList.contains(i));
 231                     }
 232                 }
 233                 subList.removeIf(pEven);
 234                 assertTrue(subList.isEmpty());
 235                 // elements outside the view should remain
 236                 CollectionAsserts.assertContents(list, listCopy);
 237             }
 238         }
 239 
 240         for (final CollectionSupplier.TestCase test : supplier.get()) {
 241             final List<Integer> list = ((List<Integer>) test.collection);
 242             trimmedSubList(list, new Callback() {
 243                 @Override
 244                 public void call(final List<Integer> list) {
 245                     final List<Integer> copy = new ArrayList<>(list);
 246                     list.removeIf(pOdd);
 247                     for (int i : list) {
 248                         assertTrue((i % 2) == 0);
 249                     }
 250                     for (int i : copy) {
 251                         if (i % 2 == 0) {
 252                             assertTrue(list.contains(i));
 253                         } else {
 254                             assertFalse(list.contains(i));
 255                         }
 256                     }
 257                 }
 258             });
 259         }
 260     }
 261 
 262     // remove the first element
 263     private void removeFirst(final List<Integer> original, final List<Integer> list, final AtomicInteger offset) {
 264         final AtomicBoolean first = new AtomicBoolean(true);
 265         list.removeIf(x -> first.getAndSet(false));
 266         CollectionAsserts.assertContents(original.subList(offset.getAndIncrement(), original.size()), list);
 267     }
 268 
 269     @Test
 270     public void testReplaceAll() throws Exception {
 271         final int scale = 3;
 272         final CollectionSupplier supplier = new CollectionSupplier(LIST_CLASSES, SIZE);
 273         for (final CollectionSupplier.TestCase test : supplier.get()) {
 274             final List<Integer> original = ((List<Integer>) test.original);
 275             final List<Integer> list = ((List<Integer>) test.collection);
 276 
 277             try {
 278                 list.replaceAll(null);
 279                 fail("expected NPE not thrown");
 280             } catch (NullPointerException npe) {}
 281             CollectionAsserts.assertContents(list, original);
 282 
 283             list.replaceAll(x -> scale * x);
 284             for (int i=0; i < original.size(); i++) {
 285                 assertTrue(list.get(i) == (scale * original.get(i)), "mismatch at index " + i);
 286             }
 287 
 288             if (original.size() > SUBLIST_SIZE) {
 289                 final List<Integer> subList = list.subList(SUBLIST_FROM, SUBLIST_TO);
 290                 subList.replaceAll(x -> x + 1);
 291                 // verify elements in view [from, to) were replaced
 292                 for (int i = 0; i < SUBLIST_SIZE; i++) {
 293                     assertTrue(subList.get(i) == ((scale * original.get(i + SUBLIST_FROM)) + 1),
 294                             "mismatch at sublist index " + i);
 295                 }
 296                 // verify that elements [0, from) remain unmodified
 297                 for (int i = 0; i < SUBLIST_FROM; i++) {
 298                     assertTrue(list.get(i) == (scale * original.get(i)),
 299                             "mismatch at original index " + i);
 300                 }
 301                 // verify that elements [to, size) remain unmodified
 302                 for (int i = SUBLIST_TO; i < list.size(); i++) {
 303                     assertTrue(list.get(i) == (scale * original.get(i)),
 304                             "mismatch at original index " + i);
 305                 }
 306             }
 307         }
 308 
 309         for (final CollectionSupplier.TestCase test : supplier.get()) {
 310             final List<Integer> list = ((List<Integer>) test.collection);
 311             trimmedSubList(list, new Callback() {
 312                 @Override
 313                 public void call(final List<Integer> list) {
 314                     final List<Integer> copy = new ArrayList<>(list);
 315                     final int offset = 5;
 316                     list.replaceAll(x -> offset + x);
 317                     for (int i=0; i < copy.size(); i++) {
 318                         assertTrue(list.get(i) == (offset + copy.get(i)), "mismatch at index " + i);
 319                     }
 320                 }
 321             });
 322         }
 323     }
 324 
 325     @Test
 326     public void testSort() throws Exception {
 327         final CollectionSupplier supplier = new CollectionSupplier(LIST_CLASSES, SIZE);
 328         for (final CollectionSupplier.TestCase test : supplier.get()) {
 329             final List<Integer> original = ((List<Integer>) test.original);
 330             final List<Integer> list = ((List<Integer>) test.collection);
 331             CollectionSupplier.shuffle(list);
 332             list.sort(Integer::compare);
 333             CollectionAsserts.assertSorted(list, Integer::compare);
 334             if (test.name.startsWith("reverse")) {
 335                 Collections.reverse(list);
 336             }
 337             CollectionAsserts.assertContents(list, original);
 338 
 339             CollectionSupplier.shuffle(list);
 340             list.sort(null);
 341             CollectionAsserts.assertSorted(list, Comparator.<Integer>naturalOrder());
 342             if (test.name.startsWith("reverse")) {
 343                 Collections.reverse(list);
 344             }
 345             CollectionAsserts.assertContents(list, original);
 346 
 347             CollectionSupplier.shuffle(list);
 348             list.sort(Comparator.<Integer>naturalOrder());
 349             CollectionAsserts.assertSorted(list, Comparator.<Integer>naturalOrder());
 350             if (test.name.startsWith("reverse")) {
 351                 Collections.reverse(list);
 352             }
 353             CollectionAsserts.assertContents(list, original);
 354 
 355             CollectionSupplier.shuffle(list);
 356             list.sort(Comparator.<Integer>reverseOrder());
 357             CollectionAsserts.assertSorted(list, Comparator.<Integer>reverseOrder());
 358             if (!test.name.startsWith("reverse")) {
 359                 Collections.reverse(list);
 360             }
 361             CollectionAsserts.assertContents(list, original);
 362 
 363             CollectionSupplier.shuffle(list);
 364             list.sort(BIT_COUNT_COMPARATOR);
 365             CollectionAsserts.assertSorted(list, BIT_COUNT_COMPARATOR);
 366             // check sort by verifying that bitCount increases and never drops
 367             int minBitCount = 0;
 368             int bitCount = 0;
 369             for (final Integer i : list) {
 370                 bitCount = Integer.bitCount(i);
 371                 assertTrue(bitCount >= minBitCount);
 372                 minBitCount = bitCount;
 373             }
 374 
 375             @SuppressWarnings("unchecked")
 376             final Class<? extends List<AtomicInteger>> type =
 377                     (Class<? extends List<AtomicInteger>>) Class.forName(test.className);
 378             final Constructor<? extends List<AtomicInteger>> defaultConstructor = type.getConstructor();
 379             final List<AtomicInteger> incomparables = (List<AtomicInteger>) defaultConstructor.newInstance();
 380 
 381             for (int i=0; i < test.original.size(); i++) {
 382                 incomparables.add(new AtomicInteger(i));
 383             }
 384             CollectionSupplier.shuffle(incomparables);
 385             incomparables.sort(ATOMIC_INTEGER_COMPARATOR);
 386             for (int i=0; i < test.original.size(); i++) {
 387                 assertEquals(i, incomparables.get(i).intValue());
 388             }
 389 
 390             if (original.size() > SUBLIST_SIZE) {
 391                 final List<Integer> copy = new ArrayList<>(list);
 392                 final List<Integer> subList = list.subList(SUBLIST_FROM, SUBLIST_TO);
 393                 CollectionSupplier.shuffle(subList);
 394                 subList.sort(Comparator.<Integer>naturalOrder());
 395                 CollectionAsserts.assertSorted(subList, Comparator.<Integer>naturalOrder());
 396                 // verify that elements [0, from) remain unmodified
 397                 for (int i = 0; i < SUBLIST_FROM; i++) {
 398                     assertTrue(list.get(i) == copy.get(i),
 399                             "mismatch at index " + i);
 400                 }
 401                 // verify that elements [to, size) remain unmodified
 402                 for (int i = SUBLIST_TO; i < list.size(); i++) {
 403                     assertTrue(list.get(i) == copy.get(i),
 404                             "mismatch at index " + i);
 405                 }
 406             }
 407         }
 408 
 409         for (final CollectionSupplier.TestCase test : supplier.get()) {
 410             final List<Integer> list = ((List<Integer>) test.collection);
 411             trimmedSubList(list, new Callback() {
 412                 @Override
 413                 public void call(final List<Integer> list) {
 414                     final List<Integer> copy = new ArrayList<>(list);
 415                     CollectionSupplier.shuffle(list);
 416                     list.sort(Comparator.<Integer>naturalOrder());
 417                     CollectionAsserts.assertSorted(list, Comparator.<Integer>naturalOrder());
 418                 }
 419             });
 420         }
 421     }
 422 
 423     @Test
 424     public void testForEachThrowsCME() throws Exception {
 425         final CollectionSupplier supplier = new CollectionSupplier(LIST_CME_CLASSES, SIZE);
 426         for (final CollectionSupplier.TestCase test : supplier.get()) {
 427             final List<Integer> list = ((List<Integer>) test.collection);
 428             if (list.size() <= 1) {
 429                 continue;
 430             }
 431             boolean gotException = false;
 432             try {
 433                 // bad predicate that modifies its list, should throw CME
 434                 list.forEach((x) -> {list.add(x);});
 435             } catch (ConcurrentModificationException cme) {
 436                 gotException = true;
 437             }
 438             if (!gotException) {
 439                 fail("expected CME was not thrown from " + test);
 440             }
 441         }
 442     }
 443 
 444     @Test
 445     public void testRemoveIfThrowsCME() throws Exception {
 446         final CollectionSupplier supplier = new CollectionSupplier(LIST_CME_CLASSES, SIZE);
 447         for (final CollectionSupplier.TestCase test : supplier.get()) {
 448             final List<Integer> list = ((List<Integer>) test.collection);
 449             if (list.size() <= 1) {
 450                 continue;
 451             }
 452             boolean gotException = false;
 453             try {
 454                 // bad predicate that modifies its list, should throw CME
 455                 list.removeIf((x) -> {return list.add(x);});
 456             } catch (ConcurrentModificationException cme) {
 457                 gotException = true;
 458             }
 459             if (!gotException) {
 460                 fail("expected CME was not thrown from " + test);
 461             }
 462         }
 463     }
 464 
 465     @Test
 466     public void testReplaceAllThrowsCME() throws Exception {
 467         final CollectionSupplier supplier = new CollectionSupplier(LIST_CME_CLASSES, SIZE);
 468         for (final CollectionSupplier.TestCase test : supplier.get()) {
 469             final List<Integer> list = ((List<Integer>) test.collection);
 470             if (list.size() <= 1) {
 471                 continue;
 472             }
 473             boolean gotException = false;
 474             try {
 475                 // bad predicate that modifies its list, should throw CME
 476                 list.replaceAll(x -> {int n = 3 * x; list.add(n); return n;});
 477             } catch (ConcurrentModificationException cme) {
 478                 gotException = true;
 479             }
 480             if (!gotException) {
 481                 fail("expected CME was not thrown from " + test);
 482             }
 483         }
 484     }
 485 
 486     @Test
 487     public void testSortThrowsCME() throws Exception {
 488         final CollectionSupplier supplier = new CollectionSupplier(LIST_CME_CLASSES, SIZE);
 489         for (final CollectionSupplier.TestCase test : supplier.get()) {
 490             final List<Integer> list = ((List<Integer>) test.collection);
 491             if (list.size() <= 1) {
 492                 continue;
 493             }
 494             boolean gotException = false;
 495             try {
 496                 // bad predicate that modifies its list, should throw CME
 497                 list.sort((x, y) -> {list.add(x); return x - y;});
 498             } catch (ConcurrentModificationException cme) {
 499                 gotException = true;
 500             }
 501             if (!gotException) {
 502                 fail("expected CME was not thrown from " + test);
 503             }
 504         }
 505     }
 506 
 507     private static final List<Integer> SLICED_EXPECTED = Arrays.asList(0, 1, 2, 3, 5, 6, 7, 8, 9);
 508     private static final List<Integer> SLICED_EXPECTED2 = Arrays.asList(0, 1, 2, 5, 6, 7, 8, 9);
 509 
 510     @DataProvider(name="shortIntListProvider", parallel=true)
 511     public static Object[][] intListCases() {
 512         final Integer[] DATA = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
 513         final List<Object[]> cases = new LinkedList<>();
 514         cases.add(new Object[] { new ArrayList<>(Arrays.asList(DATA)) });
 515         cases.add(new Object[] { new LinkedList<>(Arrays.asList(DATA)) });
 516         cases.add(new Object[] { new Vector<>(Arrays.asList(DATA)) });
 517         cases.add(new Object[] { new CopyOnWriteArrayList<>(Arrays.asList(DATA)) });
 518         cases.add(new Object[] { new ExtendsAbstractList<>(Arrays.asList(DATA)) });
 519         return cases.toArray(new Object[0][cases.size()]);
 520     }
 521 
 522     @Test(dataProvider = "shortIntListProvider")
 523     public void testRemoveIfFromSlice(final List<Integer> list) throws Exception {
 524         final List<Integer> sublist = list.subList(3, 6);
 525         assertTrue(sublist.removeIf(x -> x == 4));
 526         CollectionAsserts.assertContents(list, SLICED_EXPECTED);
 527 
 528         final List<Integer> sublist2 = list.subList(2, 5);
 529         assertTrue(sublist2.removeIf(x -> x == 3));
 530         CollectionAsserts.assertContents(list, SLICED_EXPECTED2);
 531     }
 532 }