1/*
2 * Copyright (c) 2012, 2014, 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
24import java.lang.Exception;
25import java.lang.Integer;
26import java.lang.Iterable;
27import java.lang.Override;
28import java.util.ArrayList;
29import java.util.Arrays;
30import java.util.LinkedList;
31import java.util.List;
32import java.util.Random;
33
34import org.testng.TestException;
35
36import static org.testng.Assert.assertTrue;
37
38import java.util.Collection;
39import java.util.Collections;
40import java.util.function.Function;
41import java.util.function.Supplier;
42
43/**
44 * @library
45 * @summary A Supplier of test cases for Collection tests
46 */
47public final class CollectionSupplier<C extends Collection<Integer>> implements Supplier<Iterable<CollectionSupplier.TestCase<C>>> {
48
49    private final List<Function<Collection<Integer>, C>> suppliers;
50    private final int size;
51
52    /**
53     * A Collection test case.
54     */
55    public static final class TestCase<C extends Collection<Integer>> {
56        /**
57         * The name of the test case.
58         */
59        public final String name;
60
61        /**
62         * The supplier of a collection
63         */
64        public Function<Collection<Integer>, C> supplier;
65
66        /**
67         * Unmodifiable reference collection, useful for comparisons.
68         */
69        public final List<Integer> expected;
70
71        /**
72         * A modifiable test collection.
73         */
74        public final C collection;
75
76        /**
77         * Create a Collection test case.
78         *
79         * @param name name of the test case
80         * @param collection the modifiable test collection
81         */
82        public TestCase(String name, Function<Collection<Integer>, C> supplier, C collection) {
83            this.name = name;
84            this.supplier = supplier;
85            this.expected = Collections.unmodifiableList(
86                Arrays.asList(collection.toArray(new Integer[0])));
87            this.collection = collection;
88        }
89
90        @Override
91        public String toString() {
92            return name + " " + collection.getClass().toString();
93        }
94    }
95
96    /**
97     * Shuffle a list using a PRNG with known seed for repeatability
98     *
99     * @param list the list to be shuffled
100     */
101    public static <E> void shuffle(final List<E> list) {
102        // PRNG with known seed for repeatable tests
103        final Random prng = new Random(13);
104        final int size = list.size();
105        for (int i = 0; i < size; i++) {
106            // random index in interval [i, size)
107            final int j = i + prng.nextInt(size - i);
108            // swap elements at indices i & j
109            final E e = list.get(i);
110            list.set(i, list.get(j));
111            list.set(j, e);
112        }
113    }
114
115    /**
116     * Create a {@code CollectionSupplier} that creates instances of specified
117     * collection suppliers of the specified size.
118     *
119     * @param suppliers the suppliers names that supply {@code Collection}
120     *        instances
121     * @param size the desired size of each collection
122     */
123    public CollectionSupplier(List<Function<Collection<Integer>, C>> suppliers, int size) {
124        this.suppliers = suppliers;
125        this.size = size;
126    }
127
128    @Override
129    public Iterable<TestCase<C>> get() {
130        final Collection<TestCase<C>> cases = new LinkedList<>();
131        for (final Function<Collection<Integer>, C> supplier : suppliers)
132            try {
133                cases.add(new TestCase<>("empty", supplier, supplier.apply(Collections.emptyList())));
134
135                cases.add(new TestCase<>("single", supplier, supplier.apply(Arrays.asList(42))));
136
137                final Collection<Integer> regular = new ArrayList<>();
138                for (int i = 0; i < size; i++) {
139                    regular.add(i);
140                }
141                cases.add(new TestCase<>("regular", supplier, supplier.apply(regular)));
142
143                final Collection<Integer> reverse = new ArrayList<>();
144                for (int i = size; i >= 0; i--) {
145                    reverse.add(i);
146                }
147                cases.add(new TestCase<>("reverse", supplier, supplier.apply(reverse)));
148
149                final Collection<Integer> odds = new ArrayList<>();
150                for (int i = 0; i < size; i++) {
151                    odds.add((i * 2) + 1);
152                }
153                cases.add(new TestCase<>("odds", supplier, supplier.apply(odds)));
154
155                final Collection<Integer> evens = new ArrayList<>();
156                for (int i = 0; i < size; i++) {
157                    evens.add(i * 2);
158                }
159                cases.add(new TestCase<>("evens", supplier, supplier.apply(evens)));
160
161                final Collection<Integer> fibonacci = new ArrayList<>();
162                int prev2 = 0;
163                int prev1 = 1;
164                for (int i = 0; i < size; i++) {
165                    final int n = prev1 + prev2;
166                    if (n < 0) { // stop on overflow
167                        break;
168                    }
169                    fibonacci.add(n);
170                    prev2 = prev1;
171                    prev1 = n;
172                }
173                cases.add(new TestCase<>("fibonacci", supplier, supplier.apply(fibonacci)));
174
175
176                boolean isStructurallyModifiable = false;
177                try {
178                    C t = supplier.apply(Collections.emptyList());
179                    t.add(1);
180                    isStructurallyModifiable = true;
181                } catch (UnsupportedOperationException e) { }
182
183                if (!isStructurallyModifiable)
184                    continue;
185
186
187                // variants where the size of the backing storage != reported size
188                // created by removing half of the elements
189                final C emptyWithSlack = supplier.apply(Collections.emptyList());
190                emptyWithSlack.add(42);
191                assertTrue(emptyWithSlack.remove(42));
192                cases.add(new TestCase<>("emptyWithSlack", supplier, emptyWithSlack));
193
194                final C singleWithSlack = supplier.apply(Collections.emptyList());
195                singleWithSlack.add(42);
196                singleWithSlack.add(43);
197                assertTrue(singleWithSlack.remove(43));
198                cases.add(new TestCase<>("singleWithSlack", supplier, singleWithSlack));
199
200                final C regularWithSlack = supplier.apply(Collections.emptyList());
201                for (int i = 0; i < (2 * size); i++) {
202                    regularWithSlack.add(i);
203                }
204                assertTrue(regularWithSlack.removeIf(x -> x < size));
205                cases.add(new TestCase<>("regularWithSlack", supplier, regularWithSlack));
206
207                final C reverseWithSlack = supplier.apply(Collections.emptyList());
208                for (int i = 2 * size; i >= 0; i--) {
209                    reverseWithSlack.add(i);
210                }
211                assertTrue(reverseWithSlack.removeIf(x -> x < size));
212                cases.add(new TestCase<>("reverseWithSlack", supplier, reverseWithSlack));
213
214                final C oddsWithSlack = supplier.apply(Collections.emptyList());
215                for (int i = 0; i < 2 * size; i++) {
216                    oddsWithSlack.add((i * 2) + 1);
217                }
218                assertTrue(oddsWithSlack.removeIf(x -> x >= size));
219                cases.add(new TestCase<>("oddsWithSlack", supplier, oddsWithSlack));
220
221                final C evensWithSlack = supplier.apply(Collections.emptyList());
222                for (int i = 0; i < 2 * size; i++) {
223                    evensWithSlack.add(i * 2);
224                }
225                assertTrue(evensWithSlack.removeIf(x -> x >= size));
226                cases.add(new TestCase<>("evensWithSlack", supplier, evensWithSlack));
227
228                final C fibonacciWithSlack = supplier.apply(Collections.emptyList());
229                prev2 = 0;
230                prev1 = 1;
231                for (int i = 0; i < size; i++) {
232                    final int n = prev1 + prev2;
233                    if (n < 0) { // stop on overflow
234                        break;
235                    }
236                    fibonacciWithSlack.add(n);
237                    prev2 = prev1;
238                    prev1 = n;
239                }
240                assertTrue(fibonacciWithSlack.removeIf(x -> x < 20));
241                cases.add(new TestCase<>("fibonacciWithSlack", supplier, fibonacciWithSlack));
242            }
243            catch (Exception failed) {
244                throw new TestException(failed);
245            }
246
247        return cases;
248    }
249
250}
251