1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License
15  */
16 
17 package libcore.java.util;
18 
19 
20 import java.util.ArrayList;
21 import java.util.Collection;
22 import java.util.Collections;
23 import java.util.Comparator;
24 import java.util.HashSet;
25 import java.util.Iterator;
26 import java.util.List;
27 import java.util.List;
28 import java.util.Locale;
29 import java.util.Spliterator;
30 import java.util.function.Consumer;
31 
32 import static java.util.Spliterator.ORDERED;
33 import static java.util.Spliterator.SIZED;
34 import static java.util.Spliterator.SUBSIZED;
35 import static junit.framework.Assert.assertEquals;
36 import static junit.framework.Assert.assertFalse;
37 import static junit.framework.Assert.assertNotNull;
38 import static junit.framework.Assert.assertNull;
39 import static junit.framework.Assert.assertTrue;
40 import static junit.framework.Assert.fail;
41 
42 public class SpliteratorTester {
runBasicIterationTests(Spliterator<T> spliterator, List<T> expectedElements)43     public static <T> void runBasicIterationTests(Spliterator<T> spliterator,
44             List<T> expectedElements) {
45         List<T> recorder = new ArrayList<T>(expectedElements.size());
46         Consumer<T> consumer = (T value) -> recorder.add(value);
47 
48         // tryAdvance.
49         boolean didAdvance = spliterator.tryAdvance(consumer);
50         assertEquals(!expectedElements.isEmpty(), didAdvance);
51 
52         // forEachRemaining.
53         spliterator.forEachRemaining(consumer);
54         assertEquals(expectedElements, recorder);
55 
56         // There should be no more elements remaining in this spliterator.
57         assertFalse(spliterator.tryAdvance(consumer));
58         spliterator.forEachRemaining((T) -> fail());
59     }
60 
runBasicIterationTests_unordered(Spliterator<T> spliterator, List<T> expectedElements, Comparator<T> comparator)61     public static <T> void runBasicIterationTests_unordered(Spliterator<T> spliterator,
62             List<T> expectedElements, Comparator<T> comparator) {
63         ArrayList<T> recorder = new ArrayList<T>(expectedElements.size());
64         Consumer<T> consumer = (T value) -> recorder.add(value);
65 
66         // tryAdvance.
67         if (expectedElements.isEmpty()) {
68             assertFalse(spliterator.tryAdvance(consumer));
69         } else {
70             assertTrue(spliterator.tryAdvance(consumer));
71             assertTrue(expectedElements.contains(recorder.get(0)));
72         }
73 
74         // forEachRemaining.
75         spliterator.forEachRemaining(consumer);
76         Collections.sort(expectedElements, comparator);
77         Collections.sort(recorder, comparator);
78         assertEquals(expectedElements, recorder);
79 
80         // There should be no more elements remaining in this spliterator.
81         assertFalse(spliterator.tryAdvance(consumer));
82         spliterator.forEachRemaining((T) -> fail());
83     }
84 
recordAndAssertBasicIteration( Spliterator<T> spliterator, ArrayList<T> recorder)85     private static <T> void recordAndAssertBasicIteration(
86             Spliterator<T> spliterator, ArrayList<T> recorder) {
87         spliterator.tryAdvance(value -> recorder.add(value));
88         spliterator.forEachRemaining(value -> recorder.add(value));
89 
90         // There shouldn't be any elements left in the spliterator.
91         assertFalse(spliterator.tryAdvance(value -> recorder.add(value)));
92         spliterator.tryAdvance(value -> fail());
93 
94         // And all subsequent splits should fail.
95         assertNull(spliterator.trySplit());
96     }
97 
testSpliteratorNPE(Spliterator<?> spliterator)98     public static void testSpliteratorNPE(Spliterator<?> spliterator) {
99         try {
100             spliterator.tryAdvance(null);
101             fail();
102         } catch (NullPointerException expected) {
103         }
104 
105         try {
106             spliterator.forEachRemaining(null);
107             fail();
108         } catch (NullPointerException expected) {
109         }
110     }
111 
runBasicSplitTests( Iterable<T> spliterable, List<T> expectedElements)112     public static <T extends Comparable<T>> void runBasicSplitTests(
113             Iterable<T> spliterable, List<T> expectedElements) {
114         runBasicSplitTests(spliterable, expectedElements, T::compareTo);
115     }
116 
runBasicSplitTests(Spliterator<T> spliterator, List<T> expectedElements, Comparator<T> comparator)117     public static <T> void runBasicSplitTests(Spliterator<T> spliterator,
118             List<T> expectedElements, Comparator<T> comparator) {
119         boolean empty = expectedElements.isEmpty();
120         ArrayList<T> recorder = new ArrayList<>();
121 
122         // Advance the original spliterator by one element.
123         boolean didAdvance = spliterator.tryAdvance(value -> recorder.add(value));
124         assertEquals(!empty, didAdvance);
125 
126         // Try splitting it.
127         Spliterator<T> split1 = spliterator.trySplit();
128         // trySplit() may always return null, but is only required to when empty
129         if (empty) {
130             assertNull(split1);
131         } else if (split1 != null) {
132             // Try to split the resulting split.
133             Spliterator<T> split1_1 = split1.trySplit();
134             Spliterator<T> split1_2 = split1.trySplit();
135             if (split1_1 != null) {
136                 recordAndAssertBasicIteration(split1_1, recorder);
137             }
138             if (split1_2 != null) {
139                 recordAndAssertBasicIteration(split1_2, recorder);
140             }
141 
142             // Iterate over the remainder of split1.
143             recordAndAssertBasicIteration(split1, recorder);
144         }
145         // Try to split the original iterator again.
146         Spliterator<T> split2 = spliterator.trySplit();
147         if (split2 != null) {
148             recordAndAssertBasicIteration(split2, recorder);
149         }
150 
151         // Record all remaining elements of the original spliterator.
152         recordAndAssertBasicIteration(spliterator, recorder);
153 
154         Collections.sort(expectedElements, comparator);
155         Collections.sort(recorder, comparator);
156         assertEquals(expectedElements, recorder);
157     }
158 
assertSupportsTrySplit(Iterable spliterable)159     public static <T> void assertSupportsTrySplit(Iterable spliterable) {
160         assertNotNull(spliterable.spliterator().trySplit());
161         // only non-empty Iterables may return a non-null value from trySplit()
162         assertTrue("Expected nonempty iterable, got " + spliterable,
163                 spliterable.iterator().hasNext());
164     }
165 
166     /**
167      * Note that the contract of trySplit() is generally quite weak (as it must be). There
168      * are no demands about when the spliterator can or cannot split itself. In general, this
169      * test is quite loose. All it does is exercise the basic methods on the splits (if any)
170      * and confirms that the union of all elements in the split is the collection that was
171      * iterated over.
172      */
runBasicSplitTests(Iterable<T> spliterable, List<T> expectedElements, Comparator<T> comparator)173     public static <T> void runBasicSplitTests(Iterable<T> spliterable,
174             List<T> expectedElements, Comparator<T> comparator) {
175         runBasicSplitTests(spliterable.spliterator(), expectedElements, comparator);
176     }
177 
toList(Iterator<T> iterator)178     private static<T> List<T> toList(Iterator<T> iterator) {
179         List<T> result = new ArrayList<>();
180         while (iterator.hasNext()) {
181             result.add(iterator.next());
182         }
183         return result;
184     }
185 
toList(Spliterator<T> spliterator)186     private static<T> List<T> toList(Spliterator<T> spliterator) {
187         List<T> result = new ArrayList<>();
188         spliterator.forEachRemaining(value -> result.add(value));
189         return result;
190     }
191 
runOrderedTests(Iterable<T> spliterable)192     public static <T> void runOrderedTests(Iterable<T> spliterable) {
193         List<T> elements = toList(spliterable.spliterator());
194         assertEquals("Ordering should be consistent", elements, toList(spliterable.spliterator()));
195 
196         // NOTE: This would fail for some Collections because of b/34757089:
197         // assertTrue(spliterable.spliterator().hasCharacteristics(ORDERED));
198 
199         if (spliterable instanceof Collection) {
200             assertEquals("ORDERED Spliterator must be consistent with Iterator: "
201                             + spliterable.getClass(), elements, toList(spliterable.iterator()));
202         }
203 
204         boolean isEmpty = !spliterable.iterator().hasNext();
205 
206         Spliterator<T> sa = spliterable.spliterator();
207         Spliterator<T> sb = spliterable.spliterator();
208         Spliterator<T> saSplit = sa.trySplit();
209         Spliterator<T> sbSplit = sb.trySplit();
210         // trySplit() may always return null, but is only required to when empty
211         if (isEmpty) {
212             assertNull(saSplit);
213             assertNull(sbSplit);
214         } else {
215             // A non-empty Iterable may still return null from trySplit();
216             // if it does, then the un-split parent spliterators (sa, sb) must
217             // each still contain all of the elements. Regardless of whether
218             // the split was successful, sa and sb must behave the consistently
219             // with each other since they came from the same Iterable.
220             if (saSplit != null) {
221                 assertEquals(toList(saSplit), toList(sbSplit));
222                 assertEquals(toList(sa), toList(sb));
223             } else {
224                 assertEquals(elements, toList(sa));
225                 assertEquals(elements, toList(sb));
226             }
227         }
228     }
229 
230     /**
231      * Checks that the specified SIZED Spliterator reports containing the
232      * specified number of elements.
233      */
runSizedTests(Spliterator<T> spliterator, int expectedSize)234     public static <T> void runSizedTests(Spliterator<T> spliterator, int expectedSize) {
235         assertHasCharacteristics(SIZED, spliterator);
236         assertEquals(expectedSize, spliterator.estimateSize());
237         assertEquals(expectedSize, spliterator.getExactSizeIfKnown());
238     }
239 
runSizedTests(Iterable<T> spliterable, int expectedSize)240     public static <T> void runSizedTests(Iterable<T> spliterable, int expectedSize) {
241         runSizedTests(spliterable.spliterator(), expectedSize);
242     }
243 
244     /**
245      * Checks that the specified Spliterator and its {@link Spliterator#trySplit()
246      * children} are SIZED and SUBSIZED and report containing the specified number
247      * of elements.
248      */
runSubSizedTests(Spliterator<T> spliterator, int expectedSize)249     public static <T> void runSubSizedTests(Spliterator<T> spliterator, int expectedSize) {
250         assertHasCharacteristics(SIZED | SUBSIZED, spliterator);
251         assertEquals(expectedSize, spliterator.estimateSize());
252         assertEquals(expectedSize, spliterator.getExactSizeIfKnown());
253 
254         Spliterator<T> child = spliterator.trySplit();
255         assertHasCharacteristics(SIZED | SUBSIZED, spliterator);
256         if (expectedSize == 0) {
257             assertNull(child);
258             assertEquals(expectedSize, spliterator.estimateSize());
259             assertEquals(expectedSize, spliterator.getExactSizeIfKnown());
260         } else {
261             assertHasCharacteristics(SIZED | SUBSIZED, child);
262             assertEquals(expectedSize, spliterator.estimateSize() + child.estimateSize());
263             assertEquals(expectedSize,
264                     spliterator.getExactSizeIfKnown() + child.getExactSizeIfKnown());
265         }
266     }
267 
runSubSizedTests(Iterable<T> spliterable, int expectedSize)268     public static <T> void runSubSizedTests(Iterable<T> spliterable, int expectedSize) {
269         runSubSizedTests(spliterable.spliterator(), expectedSize);
270     }
271 
runDistinctTests(Iterable<T> spliterable)272     public static <T> void runDistinctTests(Iterable<T> spliterable) {
273         HashSet<T> distinct = new HashSet<>();
274         ArrayList<T> allElements = new ArrayList<>();
275 
276         Spliterator<T> spliterator = spliterable.spliterator();
277         Spliterator<T> split1 = spliterator.trySplit();
278 
279         // First test that iterating via the spliterator using forEachRemaining
280         // yields distinct elements.
281         spliterator.forEachRemaining(value -> { distinct.add(value); allElements.add(value); });
282         // trySplit() may return null, even when non-empty
283         if (split1 != null) {
284             split1.forEachRemaining(value -> { distinct.add(value); allElements.add(value); });
285         }
286         assertEquals(distinct.size(), allElements.size());
287 
288         distinct.clear();
289         allElements.clear();
290         spliterator = spliterable.spliterator();
291         split1 = spliterator.trySplit();
292 
293         // Then test whether using tryAdvance yields the same results.
294         while (spliterator.tryAdvance(value -> { distinct.add(value); allElements.add(value); })) {
295         }
296 
297         // trySplit() may return null, even when non-empty
298         if (split1 != null) {
299             while (split1.tryAdvance(value -> { distinct.add(value); allElements.add(value); })) {
300             }
301         }
302 
303         assertEquals(distinct.size(), allElements.size());
304     }
305 
runSortedTests(Iterable<T> spliterable, Comparator<T> comparator)306     public static <T> void runSortedTests(Iterable<T> spliterable, Comparator<T> comparator) {
307         Spliterator<T> spliterator = spliterable.spliterator();
308         Spliterator<T> split1 = spliterator.trySplit();
309 
310         ArrayList<T> elements = new ArrayList<>();
311         spliterator.forEachRemaining(value -> elements.add(value));
312 
313         ArrayList<T> sortedElements = new ArrayList<>(elements);
314         Collections.sort(sortedElements, comparator);
315         assertEquals(elements, sortedElements);
316 
317         elements.clear();
318 
319         split1.forEachRemaining(value -> elements.add(value));
320         sortedElements = new ArrayList<>(elements);
321         Collections.sort(sortedElements, comparator);
322         assertEquals(elements, sortedElements);
323     }
324 
runSortedTests(Iterable<T> spliterable)325     public static <T extends Comparable<T>> void runSortedTests(Iterable<T> spliterable) {
326         runSortedTests(spliterable, T::compareTo);
327     }
328 
assertHasCharacteristics(int expectedCharacteristics, Spliterator<?> spliterator)329     public static void assertHasCharacteristics(int expectedCharacteristics,
330             Spliterator<?> spliterator) {
331         int actualCharacteristics = spliterator.characteristics();
332         String msg = String.format(Locale.US,
333                 "Expected expectedCharacteristics containing 0x%x, got 0x%x",
334                 expectedCharacteristics, actualCharacteristics);
335         assertTrue(msg, spliterator.hasCharacteristics(expectedCharacteristics));
336     }
337 }
338