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.lang.Exception;
  25 import java.lang.Integer;
  26 import java.lang.Iterable;
  27 import java.lang.Override;
  28 import java.util.Arrays;
  29 import java.util.LinkedList;
  30 import java.util.List;
  31 import java.util.Random;
  32 import java.util.Set;
  33 
  34 import org.testng.TestException;
  35 
  36 import static org.testng.Assert.assertTrue;
  37 
  38 import java.lang.reflect.Constructor;
  39 import java.util.Collection;
  40 import java.util.Collections;
  41 import java.util.function.Supplier;
  42 
  43 /**
  44  * @library
  45  * @summary A Supplier of test cases for Collection tests
  46  */
  47 public final class CollectionSupplier implements Supplier<Iterable<CollectionSupplier.TestCase>> {
  48 
  49     private final String[] classNames;
  50     private final int size;
  51 
  52     /**
  53      * A Collection test case.
  54      */
  55     public static final class TestCase {
  56 
  57         /**
  58          * The name of the test case.
  59          */
  60         public final String name;
  61 
  62         /**
  63          * Class name of the instantiated Collection.
  64          */
  65         public final String className;
  66 
  67         /**
  68          * Unmodifiable reference collection, useful for comparisons.
  69          */
  70         public final Collection<Integer> original;
  71 
  72         /**
  73          * A modifiable test collection.
  74          */
  75         public final Collection<Integer> collection;
  76 
  77         /**
  78          * Create a Collection test case.
  79          * @param name name of the test case
  80          * @param className class name of the instantiated collection
  81          * @param original reference collection
  82          * @param collection the modifiable test collection
  83          */
  84         public TestCase(String name, String className,
  85                 Collection<Integer> original, Collection<Integer> collection) {
  86             this.name = name;
  87             this.className = className;
  88             this.original =
  89                     List.class.isAssignableFrom(original.getClass()) ?
  90                     Collections.unmodifiableList((List<Integer>) original) :
  91                     Set.class.isAssignableFrom(original.getClass()) ?
  92                     Collections.unmodifiableSet((Set<Integer>) original) :
  93                     Collections.unmodifiableCollection(original);
  94             this.collection = collection;
  95         }
  96 
  97         @Override
  98         public String toString() {
  99             return name + " " + className;
 100         }
 101     }
 102 
 103     /**
 104      * Shuffle a list using a PRNG with known seed for repeatability
 105      * @param list the list to be shuffled
 106      */
 107     public static <E> void shuffle(final List<E> list) {
 108         // PRNG with known seed for repeatable tests
 109         final Random prng = new Random(13);
 110         final int size = list.size();
 111         for (int i=0; i < size; i++) {
 112             // random index in interval [i, size)
 113             final int j = i + prng.nextInt(size - i);
 114             // swap elements at indices i & j
 115             final E e = list.get(i);
 116             list.set(i, list.get(j));
 117             list.set(j, e);
 118         }
 119     }
 120 
 121     /**
 122      * Create a {@code Supplier} that creates instances of specified collection
 123      * classes of specified length.
 124      *
 125      * @param classNames class names that implement {@code Collection}
 126      * @param size the desired size of each collection
 127      */
 128     public CollectionSupplier(String[] classNames, int size) {
 129         this.classNames = Arrays.copyOf(classNames, classNames.length);
 130         this.size = size;
 131     }
 132 
 133     @Override
 134     public Iterable<TestCase> get() {
 135         try {
 136             return getThrows();
 137         } catch (Exception e) {
 138             throw new TestException(e);
 139         }
 140     }
 141 
 142     private Iterable<TestCase> getThrows() throws Exception {
 143         final Collection<TestCase> collections = new LinkedList<>();
 144         for (final String className : classNames) {
 145             @SuppressWarnings("unchecked")
 146             final Class<? extends Collection<Integer>> type =
 147                     (Class<? extends Collection<Integer>>) Class.forName(className);
 148             final Constructor<? extends Collection<Integer>>
 149                     defaultConstructor = type.getConstructor();
 150             final Constructor<? extends Collection<Integer>>
 151                     copyConstructor = type.getConstructor(Collection.class);
 152 
 153             final Collection<Integer> empty = defaultConstructor.newInstance();
 154             collections.add(new TestCase("empty",
 155                     className,
 156                     copyConstructor.newInstance(empty),
 157                     empty));
 158 
 159             final Collection<Integer> single = defaultConstructor.newInstance();
 160             single.add(42);
 161             collections.add(new TestCase("single",
 162                     className,
 163                     copyConstructor.newInstance(single),
 164                     single));
 165 
 166             final Collection<Integer> regular = defaultConstructor.newInstance();
 167             for (int i=0; i < size; i++) {
 168                 regular.add(i);
 169             }
 170             collections.add(new TestCase("regular",
 171                     className,
 172                     copyConstructor.newInstance(regular),
 173                     regular));
 174 
 175             final Collection<Integer> reverse = defaultConstructor.newInstance();
 176             for (int i=size; i >= 0; i--) {
 177                 reverse.add(i);
 178             }
 179             collections.add(new TestCase("reverse",
 180                     className,
 181                     copyConstructor.newInstance(reverse),
 182                     reverse));
 183 
 184             final Collection<Integer> odds = defaultConstructor.newInstance();
 185             for (int i=0; i < size; i++) {
 186                 odds.add((i * 2) + 1);
 187             }
 188             collections.add(new TestCase("odds",
 189                     className,
 190                     copyConstructor.newInstance(odds),
 191                     odds));
 192 
 193             final Collection<Integer> evens = defaultConstructor.newInstance();
 194             for (int i=0; i < size; i++) {
 195                 evens.add(i * 2);
 196             }
 197             collections.add(new TestCase("evens",
 198                     className,
 199                     copyConstructor.newInstance(evens),
 200                     evens));
 201 
 202             final Collection<Integer> fibonacci = defaultConstructor.newInstance();
 203             int prev2 = 0;
 204             int prev1 = 1;
 205             for (int i=0; i < size; i++) {
 206                 final int n = prev1 + prev2;
 207                 if (n < 0) { // stop on overflow
 208                     break;
 209                 }
 210                 fibonacci.add(n);
 211                 prev2 = prev1;
 212                 prev1 = n;
 213             }
 214             collections.add(new TestCase("fibonacci",
 215                     className,
 216                     copyConstructor.newInstance(fibonacci),
 217                     fibonacci));
 218 
 219             // variants where the size of the backing storage != reported size
 220             // created by removing half of the elements
 221 
 222             final Collection<Integer> emptyWithSlack = defaultConstructor.newInstance();
 223             emptyWithSlack.add(42);
 224             assertTrue(emptyWithSlack.remove(42));
 225             collections.add(new TestCase("emptyWithSlack",
 226                     className,
 227                     copyConstructor.newInstance(emptyWithSlack),
 228                     emptyWithSlack));
 229 
 230             final Collection<Integer> singleWithSlack = defaultConstructor.newInstance();
 231             singleWithSlack.add(42);
 232             singleWithSlack.add(43);
 233             assertTrue(singleWithSlack.remove(43));
 234             collections.add(new TestCase("singleWithSlack",
 235                     className,
 236                     copyConstructor.newInstance(singleWithSlack),
 237                     singleWithSlack));
 238 
 239             final Collection<Integer> regularWithSlack = defaultConstructor.newInstance();
 240             for (int i=0; i < (2 * size); i++) {
 241                 regularWithSlack.add(i);
 242             }
 243             assertTrue(regularWithSlack.removeIf((x) -> {return x >= size;}));
 244             collections.add(new TestCase("regularWithSlack",
 245                     className,
 246                     copyConstructor.newInstance(regularWithSlack),
 247                     regularWithSlack));
 248 
 249             final Collection<Integer> reverseWithSlack = defaultConstructor.newInstance();
 250             for (int i=2 * size; i >= 0; i--) {
 251                 reverseWithSlack.add(i);
 252             }
 253             assertTrue(reverseWithSlack.removeIf((x) -> {return x < size;}));
 254             collections.add(new TestCase("reverseWithSlack",
 255                     className,
 256                     copyConstructor.newInstance(reverseWithSlack),
 257                     reverseWithSlack));
 258 
 259             final Collection<Integer> oddsWithSlack = defaultConstructor.newInstance();
 260             for (int i = 0; i < 2 * size; i++) {
 261                 oddsWithSlack.add((i * 2) + 1);
 262             }
 263             assertTrue(oddsWithSlack.removeIf((x) -> {return x >= size;}));
 264             collections.add(new TestCase("oddsWithSlack",
 265                     className,
 266                     copyConstructor.newInstance(oddsWithSlack),
 267                     oddsWithSlack));
 268 
 269             final Collection<Integer> evensWithSlack = defaultConstructor.newInstance();
 270             for (int i = 0; i < 2 * size; i++) {
 271                 evensWithSlack.add(i * 2);
 272             }
 273             assertTrue(evensWithSlack.removeIf((x) -> {return x >= size;}));
 274             collections.add(new TestCase("evensWithSlack",
 275                     className,
 276                     copyConstructor.newInstance(evensWithSlack),
 277                     evensWithSlack));
 278 
 279             final Collection<Integer> fibonacciWithSlack = defaultConstructor.newInstance();
 280             prev2 = 0;
 281             prev1 = 1;
 282             for (int i=0; i < size; i++) {
 283                 final int n = prev1 + prev2;
 284                 if (n < 0) { // stop on overflow
 285                     break;
 286                 }
 287                 fibonacciWithSlack.add(n);
 288                 prev2 = prev1;
 289                 prev1 = n;
 290             }
 291             assertTrue(fibonacciWithSlack.removeIf((x) -> {return x < 20;}));
 292             collections.add(new TestCase("fibonacciWithSlack",
 293                     className,
 294                     copyConstructor.newInstance(fibonacciWithSlack),
 295                     fibonacciWithSlack));
 296 
 297         }
 298 
 299         return collections;
 300     }
 301 
 302 }