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.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 import java.util.Collections;
  27 import java.util.Comparator;
  28 import java.util.List;
  29 import java.util.LinkedList;
  30 import java.util.ListIterator;
  31 import java.util.concurrent.atomic.AtomicInteger;
  32 
  33 import org.testng.annotations.Test;
  34 
  35 import java.lang.reflect.Constructor;
  36 import java.util.ConcurrentModificationException;
  37 import java.util.function.Predicate;
  38 
  39 import static org.testng.Assert.*;
  40 
  41 /**
  42  * @test
  43  * @library testlibrary
  44  * @build CollectionAsserts CollectionSupplier
  45  * @run testng ListExtensionMethodsTest
  46  * @summary Unit tests for extension methods on List
  47  */
  48 public class ListExtensionMethodsTest {
  49 
  50     private static final String[] LIST_CLASSES = {
  51         "java.util.ArrayList",
  52         "java.util.LinkedList",
  53         "java.util.Vector",
  54         "java.util.concurrent.CopyOnWriteArrayList"
  55     };
  56 
  57     private static final String[] LIST_CME_CLASSES = {
  58         "java.util.ArrayList",
  59         "java.util.Vector"
  60     };
  61 
  62     private static final Predicate<Integer> P_EVEN = x -> 0 == x % 2;
  63     private static final Predicate<Integer> P_ODD = x -> 0 != x % 2;
  64 
  65     private static final Comparator<Integer> BIT_COUNT_COMPARATOR =
  66             (x, y) -> Integer.bitCount(x) - Integer.bitCount(y);
  67 
  68     private static final Comparator<AtomicInteger> ATOMIC_INTEGER_COMPARATOR =
  69             (x, y) -> x.intValue() - y.intValue();
  70 
  71     @Test
  72     public void testUnmodifiable() throws Exception {
  73         final CollectionSupplier supplier = new CollectionSupplier(LIST_CLASSES, 10);
  74         for (final CollectionSupplier.TestCase test : supplier.get()) {
  75             final List<Integer> list = ((List<Integer>) test.collection);
  76             final List<Integer> unmodifiable = Collections.unmodifiableList(list);
  77             // forEach does not modify, it should not throw UnsupportedOperationException
  78             unmodifiable.forEach((x) -> {});
  79             try {
  80                 unmodifiable.removeAll(P_EVEN);
  81                 fail("removeAll on unmodifiable list did not throw exception");
  82             } catch (UnsupportedOperationException ignore) {}
  83             try {
  84                 unmodifiable.replaceAll(x -> x);
  85                 fail("replaceAll on unmodifiable list did not throw exception");
  86             } catch (UnsupportedOperationException ignore) {}
  87             try {
  88                 unmodifiable.sort((x, y) -> x - y);
  89                 fail("sort on unmodifiable list did not throw exception");
  90             } catch (UnsupportedOperationException ignore) {}
  91         }
  92     }
  93 
  94     @Test
  95     public void testForNullPointerException() throws Exception {
  96         final CollectionSupplier supplier = new CollectionSupplier(LIST_CLASSES, 10);
  97         for (final CollectionSupplier.TestCase test : supplier.get()) {
  98             final List<Integer> list = ((List<Integer>) test.collection);
  99             try {
 100                 list.forEach(null);
 101                 fail("forEach with null Block did not throw NPE");
 102             } catch (NullPointerException nx) {}
 103             try {
 104                 list.removeAll((Predicate<? super Integer>) null);
 105                 fail("removeAll with null Predicate did not throw NPE");
 106             } catch (NullPointerException nx) {}
 107             try {
 108                 list.replaceAll(null);
 109                 fail("replaceAll with null UnaryOperator did not throw NPE");
 110             } catch (NullPointerException nx) {}
 111             try {
 112                 list.sort(null);
 113                 fail("sort with null Comparator did not throw NPE");
 114             } catch (NullPointerException nx) {}
 115         }
 116     }
 117 
 118     @Test
 119     public void testForEach() throws Exception {
 120         final CollectionSupplier supplier = new CollectionSupplier(LIST_CLASSES, 1000);
 121         for (final CollectionSupplier.TestCase test : supplier.get()) {
 122             final List<Integer> original = ((List<Integer>) test.original);
 123             final List<Integer> list = ((List<Integer>) test.collection);
 124             final List<Integer> actual = new LinkedList<>();
 125             list.forEach(actual::add);
 126             CollectionAsserts.assertContents(actual, list);
 127             CollectionAsserts.assertContents(actual, original);
 128         }
 129     }
 130 
 131     @Test
 132     public void testRemoveAll() throws Exception {
 133         final CollectionSupplier supplier = new CollectionSupplier(LIST_CLASSES, 1000);
 134         for (final CollectionSupplier.TestCase test : supplier.get()) {
 135             final List<Integer> original = ((List<Integer>) test.original);
 136             final List<Integer> list = ((List<Integer>) test.collection);
 137             list.removeAll(P_ODD);
 138             for (final int i : list) {
 139                 assertTrue((i % 2) == 0);
 140             }
 141             final ListIterator<Integer> li = list.listIterator();
 142             for (final int i : original) {
 143                 if (i % 2 == 0) {
 144                     assertEquals(li.next().intValue(), i);
 145                 }
 146             }
 147             assertFalse(li.hasNext());
 148             list.removeAll(P_EVEN);
 149             assertTrue(list.isEmpty());
 150         }
 151     }
 152 
 153     @Test
 154     public void testReplaceAll() throws Exception {
 155         final int scale = 3;
 156         final CollectionSupplier supplier = new CollectionSupplier(LIST_CLASSES, 1000);
 157         for (final CollectionSupplier.TestCase test : supplier.get()) {
 158             final List<Integer> original = ((List<Integer>) test.original);
 159             final List<Integer> list = ((List<Integer>) test.collection);
 160             list.replaceAll(x -> scale * x);
 161             for (int i=0; i < original.size(); i++) {
 162                 assertTrue(list.get(i) == (scale * original.get(i)), "mismatch at index " + i);
 163             }
 164         }
 165     }
 166 
 167     @Test
 168     public void testSort() throws Exception {
 169         final CollectionSupplier supplier = new CollectionSupplier(LIST_CLASSES, 1000);
 170         for (final CollectionSupplier.TestCase test : supplier.get()) {
 171             final List<Integer> original = ((List<Integer>) test.original);
 172             final List<Integer> list = ((List<Integer>) test.collection);
 173             CollectionSupplier.shuffle(list);
 174             list.sort(Integer::compare);
 175             CollectionAsserts.assertSorted(list, Integer::compare);
 176             if (test.name.startsWith("reverse")) {
 177                 Collections.reverse(list);
 178             }
 179             CollectionAsserts.assertContents(list, original);
 180 
 181             /* disabled until java.util.Comparators is availlable
 182             CollectionSupplier.shuffle(list);
 183             list.sort(Comparators.<Integer>naturalOrder());
 184             CollectionAsserts.assertSorted(list, Comparators.<Integer>naturalOrder());
 185             if (test.name.startsWith("reverse")) {
 186                 Collections.reverse(list);
 187             }
 188             CollectionAsserts.assertContents(list, original);
 189 
 190             CollectionSupplier.shuffle(list);
 191             list.sort(Comparators.<Integer>reverseOrder());
 192             CollectionAsserts.assertSorted(list, Comparators.<Integer>reverseOrder());
 193             if (!test.name.startsWith("reverse")) {
 194                 Collections.reverse(list);
 195             }
 196             CollectionAsserts.assertContents(list, original);
 197             */
 198 
 199             CollectionSupplier.shuffle(list);
 200             list.sort(BIT_COUNT_COMPARATOR);
 201             CollectionAsserts.assertSorted(list, BIT_COUNT_COMPARATOR);
 202             // check sort by verifying that bitCount increases and never drops
 203             int minBitCount = 0;
 204             int bitCount;
 205             for (final Integer i : list) {
 206                 bitCount = Integer.bitCount(i);
 207                 assertTrue(bitCount >= minBitCount);
 208                 minBitCount = bitCount;
 209             }
 210 
 211             @SuppressWarnings("unchecked")
 212             final Class<? extends List<AtomicInteger>> type =
 213                     (Class<? extends List<AtomicInteger>>) Class.forName(test.className);
 214             final Constructor<? extends List<AtomicInteger>> defaultConstructor = type.getConstructor();
 215             final List<AtomicInteger> incomparables = defaultConstructor.newInstance();
 216 
 217             for (int i=0; i < test.original.size(); i++) {
 218                 incomparables.add(new AtomicInteger(i));
 219             }
 220             CollectionSupplier.shuffle(incomparables);
 221             incomparables.sort(ATOMIC_INTEGER_COMPARATOR);
 222             for (int i=0; i < test.original.size(); i++) {
 223                 assertEquals(i, incomparables.get(i).intValue());
 224             }
 225         }
 226     }
 227 
 228     @Test
 229     public void testRemoveAllThrowsCME() throws Exception {
 230         final CollectionSupplier supplier = new CollectionSupplier(LIST_CME_CLASSES, 100);
 231         for (final CollectionSupplier.TestCase test : supplier.get()) {
 232             final List<Integer> list = ((List<Integer>) test.collection);
 233             if (list.size() <= 1) {
 234                 continue;
 235             }
 236             boolean gotException = false;
 237             try {
 238                 // bad predicate that modifies its list, should throw CME
 239                 list.removeAll(list::add);
 240             } catch (ConcurrentModificationException cme) {
 241                 gotException = true;
 242             }
 243             if (!gotException) {
 244                 fail("expected CME was not thrown from " + test);
 245             }
 246         }
 247     }
 248 
 249     @Test
 250     public void testReplaceAllThrowsCME() throws Exception {
 251         final CollectionSupplier supplier = new CollectionSupplier(LIST_CME_CLASSES, 100);
 252         for (final CollectionSupplier.TestCase test : supplier.get()) {
 253             final List<Integer> list = ((List<Integer>) test.collection);
 254             if (list.size() <= 1) {
 255                 continue;
 256             }
 257             boolean gotException = false;
 258             try {
 259                 // bad predicate that modifies its list, should throw CME
 260                 list.replaceAll(x -> {int n = 3 * x; list.add(n); return n;});
 261             } catch (ConcurrentModificationException cme) {
 262                 gotException = true;
 263             }
 264             if (!gotException) {
 265                 fail("expected CME was not thrown from " + test);
 266             }
 267         }
 268     }
 269 
 270     @Test
 271     public void testSortThrowsCME() throws Exception {
 272         final CollectionSupplier supplier = new CollectionSupplier(LIST_CME_CLASSES, 100);
 273         for (final CollectionSupplier.TestCase test : supplier.get()) {
 274             final List<Integer> list = ((List<Integer>) test.collection);
 275             if (list.size() <= 1) {
 276                 continue;
 277             }
 278             boolean gotException = false;
 279             try {
 280                 // bad predicate that modifies its list, should throw CME
 281                 list.sort((x, y) -> {list.add(x); return x - y;});
 282             } catch (ConcurrentModificationException cme) {
 283                 gotException = true;
 284             }
 285             if (!gotException) {
 286                 fail("expected CME was not thrown from " + test);
 287             }
 288         }
 289     }
 290 
 291 }