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