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