1 /*
2  * Copyright (c) 2012, 2021, 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 package org.openjdk.tests.java.util.stream;
24 
25 import org.openjdk.testlib.java.util.stream.LambdaTestHelpers;
26 import org.openjdk.testlib.java.util.stream.OpTestCase;
27 import org.openjdk.testlib.java.util.stream.StreamTestDataProvider;
28 import org.openjdk.testlib.java.util.stream.TestData;
29 
30 import org.testng.annotations.Test;
31 
32 import java.util.*;
33 import java.util.concurrent.atomic.AtomicInteger;
34 import java.util.function.Consumer;
35 import java.util.function.Function;
36 import java.util.stream.Collectors;
37 import java.util.stream.DoubleStream;
38 import java.util.stream.IntStream;
39 import java.util.stream.LongStream;
40 import java.util.stream.Stream;
41 import java.util.stream.StreamSupport;
42 
43 import static org.openjdk.testlib.java.util.stream.LambdaTestHelpers.*;
44 
45 import android.platform.test.annotations.LargeTest;
46 
47 /*
48  * @test
49  * @bug 8148250 8265029
50  */
51 @Test
52 public class SliceOpTest extends OpTestCase {
53 
testSkip()54     public void testSkip() {
55         assertCountSum(countTo(0).stream().skip(0), 0, 0);
56         assertCountSum(countTo(0).stream().skip(4), 0, 0);
57         assertCountSum(countTo(4).stream().skip(4), 0, 0);
58         assertCountSum(countTo(4).stream().skip(2), 2, 7);
59         assertCountSum(countTo(4).stream().skip(0), 4, 10);
60 
61         assertCountSum(countTo(0).parallelStream().skip(0), 0, 0);
62         assertCountSum(countTo(0).parallelStream().skip(4), 0, 0);
63         assertCountSum(countTo(4).parallelStream().skip(4), 0, 0);
64         assertCountSum(countTo(4).parallelStream().skip(2), 2, 7);
65         assertCountSum(countTo(4).parallelStream().skip(0), 4, 10);
66 
67         exerciseOps(Collections.emptyList(), s -> s.skip(0), Collections.emptyList());
68         exerciseOps(Collections.emptyList(), s -> s.skip(10), Collections.emptyList());
69 
70         exerciseOps(countTo(1), s -> s.skip(0), countTo(1));
71         exerciseOps(countTo(1), s -> s.skip(1), Collections.emptyList());
72         exerciseOps(countTo(100), s -> s.skip(0), countTo(100));
73         exerciseOps(countTo(100), s -> s.skip(10), range(11, 100));
74         exerciseOps(countTo(100), s -> s.skip(100), Collections.emptyList());
75         exerciseOps(countTo(100), s -> s.skip(200), Collections.emptyList());
76     }
77 
testLimit()78     public void testLimit() {
79         assertCountSum(countTo(0).stream().limit(4), 0, 0);
80         assertCountSum(countTo(2).stream().limit(4), 2, 3);
81         assertCountSum(countTo(4).stream().limit(4), 4, 10);
82         assertCountSum(countTo(8).stream().limit(4), 4, 10);
83 
84         assertCountSum(countTo(0).parallelStream().limit(4), 0, 0);
85         assertCountSum(countTo(2).parallelStream().limit(4), 2, 3);
86         assertCountSum(countTo(4).parallelStream().limit(4), 4, 10);
87         assertCountSum(countTo(8).parallelStream().limit(4), 4, 10);
88 
89         exerciseOps(Collections.emptyList(), s -> s.limit(0), Collections.emptyList());
90         exerciseOps(Collections.emptyList(), s -> s.limit(10), Collections.emptyList());
91         exerciseOps(countTo(1), s -> s.limit(0), Collections.emptyList());
92         exerciseOps(countTo(1), s -> s.limit(1), countTo(1));
93         exerciseOps(countTo(100), s -> s.limit(0), Collections.emptyList());
94         exerciseOps(countTo(100), s -> s.limit(10), countTo(10));
95         exerciseOps(countTo(100), s -> s.limit(10).limit(10), countTo(10));
96         exerciseOps(countTo(100), s -> s.limit(100), countTo(100));
97         exerciseOps(countTo(100), s -> s.limit(100).limit(10), countTo(10));
98         exerciseOps(countTo(100), s -> s.limit(200), countTo(100));
99     }
100 
testSkipLimit()101     public void testSkipLimit() {
102         exerciseOps(Collections.emptyList(), s -> s.skip(0).limit(0), Collections.emptyList());
103         exerciseOps(Collections.emptyList(), s -> s.skip(0).limit(10), Collections.emptyList());
104         exerciseOps(Collections.emptyList(), s -> s.skip(10).limit(0), Collections.emptyList());
105         exerciseOps(Collections.emptyList(), s -> s.skip(10).limit(10), Collections.emptyList());
106 
107         exerciseOps(countTo(100), s -> s.skip(0).limit(100), countTo(100));
108         exerciseOps(countTo(100), s -> s.skip(0).limit(10), countTo(10));
109         exerciseOps(countTo(100), s -> s.skip(0).limit(0), Collections.emptyList());
110         exerciseOps(countTo(100), s -> s.skip(10).limit(100), range(11, 100));
111         exerciseOps(countTo(100), s -> s.skip(10).limit(10), range(11, 20));
112         exerciseOps(countTo(100), s -> s.skip(10).limit(0), Collections.emptyList());
113         exerciseOps(countTo(100), s -> s.skip(100).limit(100), Collections.emptyList());
114         exerciseOps(countTo(100), s -> s.skip(100).limit(10), Collections.emptyList());
115         exerciseOps(countTo(100), s -> s.skip(100).limit(0), Collections.emptyList());
116         exerciseOps(countTo(100), s -> s.skip(200).limit(100), Collections.emptyList());
117         exerciseOps(countTo(100), s -> s.skip(200).limit(10), Collections.emptyList());
118         exerciseOps(countTo(100), s -> s.skip(200).limit(0), Collections.emptyList());
119     }
120 
testSlice()121     public void testSlice() {
122         exerciseOps(Collections.emptyList(), s -> s.skip(0).limit(0), Collections.emptyList());
123         exerciseOps(Collections.emptyList(), s -> s.skip(0).limit(10), Collections.emptyList());
124         exerciseOps(Collections.emptyList(), s -> s.skip(10).limit(10), Collections.emptyList());
125         exerciseOps(Collections.emptyList(), s -> s.skip(10).limit(20), Collections.emptyList());
126 
127         exerciseOps(countTo(100), s -> s.skip(0).limit(100), countTo(100));
128         exerciseOps(countTo(100), s -> s.skip(0).limit(10), countTo(10));
129         exerciseOps(countTo(100), s -> s.skip(0).limit(0), Collections.emptyList());
130         exerciseOps(countTo(100), s -> s.skip(10).limit(100), range(11, 100));
131         exerciseOps(countTo(100), s -> s.skip(10).limit(10), range(11, 20));
132         exerciseOps(countTo(100), s -> s.skip(10).limit(0), Collections.emptyList());
133         exerciseOps(countTo(100), s -> s.skip(100).limit(100), Collections.emptyList());
134         exerciseOps(countTo(100), s -> s.skip(100).limit(10), Collections.emptyList());
135         exerciseOps(countTo(100), s -> s.skip(100).limit(0), Collections.emptyList());
136         exerciseOps(countTo(100), s -> s.skip(200).limit(100), Collections.emptyList());
137         exerciseOps(countTo(100), s -> s.skip(200).limit(10), Collections.emptyList());
138         exerciseOps(countTo(100), s -> s.skip(200).limit(0), Collections.emptyList());
139     }
140 
sliceSize(int dataSize, int skip, int limit)141     private int sliceSize(int dataSize, int skip, int limit) {
142         int size = Math.max(0, dataSize - skip);
143         if (limit >= 0)
144             size = Math.min(size, limit);
145         return size;
146     }
147 
sliceSize(int dataSize, int skip)148     private int sliceSize(int dataSize, int skip) {
149         return Math.max(0, dataSize - skip);
150     }
151 
152     @LargeTest
153     @Test(dataProvider = "StreamTestData<Integer>", dataProviderClass = StreamTestDataProvider.class,
154           groups = { "serialization-hostile" })
testSkipOps(String name, TestData.OfRef<Integer> data)155     public void testSkipOps(String name, TestData.OfRef<Integer> data) {
156         List<Integer> skips = sizes(data.size());
157 
158         for (int s : skips) {
159             setContext("skip", s);
160             testSliceMulti(data,
161                            sliceSize(data.size(), s),
162                            st -> st.skip(s),
163                            st -> st.skip(s),
164                            st -> st.skip(s),
165                            st -> st.skip(s));
166 
167             testSliceMulti(data,
168                            sliceSize(sliceSize(data.size(), s), s/2),
169                            st -> st.skip(s).skip(s / 2),
170                            st -> st.skip(s).skip(s / 2),
171                            st -> st.skip(s).skip(s / 2),
172                            st -> st.skip(s).skip(s / 2));
173         }
174     }
175 
176     @LargeTest
177     @Test(dataProvider = "StreamTestData<Integer>", dataProviderClass = StreamTestDataProvider.class,
178           groups = { "serialization-hostile" })
testSkipLimitOps(String name, TestData.OfRef<Integer> data)179     public void testSkipLimitOps(String name, TestData.OfRef<Integer> data) {
180         List<Integer> skips = sizes(data.size());
181         List<Integer> limits = skips;
182 
183         for (int s : skips) {
184             setContext("skip", s);
185             for (int l : limits) {
186                 setContext("limit", l);
187                 testSliceMulti(data,
188                                sliceSize(sliceSize(data.size(), s), 0, l),
189                                st -> st.skip(s).limit(l),
190                                st -> st.skip(s).limit(l),
191                                st -> st.skip(s).limit(l),
192                                st -> st.skip(s).limit(l));
193             }
194         }
195     }
196 
197     @Test(groups = { "serialization-hostile" })
testSkipLimitOpsWithNonSplittingSpliterator()198     public void testSkipLimitOpsWithNonSplittingSpliterator() {
199         class NonSplittingNotSubsizedOrderedSpliterator<T> implements Spliterator<T> {
200             Spliterator<T> s;
201 
202             NonSplittingNotSubsizedOrderedSpliterator(Spliterator<T> s) {
203                 assert s.hasCharacteristics(Spliterator.ORDERED);
204                 this.s = s;
205             }
206 
207             @Override
208             public boolean tryAdvance(Consumer<? super T> action) {
209                 return s.tryAdvance(action);
210             }
211 
212             @Override
213             public void forEachRemaining(Consumer<? super T> action) {
214                 s.forEachRemaining(action);
215             }
216 
217             @Override
218             public Spliterator<T> trySplit() {
219                 return null;
220             }
221 
222             @Override
223             public long estimateSize() {
224                 return s.estimateSize();
225             }
226 
227             @Override
228             public int characteristics() {
229                 return s.characteristics() & ~(Spliterator.SUBSIZED);
230             }
231 
232             @Override
233             public Comparator<? super T> getComparator() {
234                 return s.getComparator();
235             }
236         }
237         List<Integer> list = IntStream.range(0, 100).boxed().collect(Collectors.toList());
238         TestData.OfRef<Integer> data = TestData.Factory.ofSupplier(
239                 "Non splitting, not SUBSIZED, ORDERED, stream",
240                 () -> StreamSupport.stream(new NonSplittingNotSubsizedOrderedSpliterator<>(list.spliterator()), false));
241 
242         testSkipLimitOps("testSkipLimitOpsWithNonSplittingSpliterator", data);
243     }
244 
245     @LargeTest
246     @Test(dataProvider = "StreamTestData<Integer>", dataProviderClass = StreamTestDataProvider.class,
247           groups = { "serialization-hostile" })
testLimitOps(String name, TestData.OfRef<Integer> data)248     public void testLimitOps(String name, TestData.OfRef<Integer> data) {
249         List<Integer> limits = sizes(data.size());
250 
251         for (int l : limits) {
252             setContext("limit", l);
253             testSliceMulti(data,
254                            sliceSize(data.size(), 0, l),
255                            st -> st.limit(l),
256                            st -> st.limit(l),
257                            st -> st.limit(l),
258                            st -> st.limit(l));
259         }
260 
261         for (int l : limits) {
262             setContext("limit", l);
263             testSliceMulti(data,
264                            sliceSize(sliceSize(data.size(), 0, l), 0, l / 2),
265                            st -> st.limit(l).limit(l / 2),
266                            st -> st.limit(l).limit(l / 2),
267                            st -> st.limit(l).limit(l / 2),
268                            st -> st.limit(l).limit(l / 2));
269         }
270     }
271 
sliceResultAsserter(Iterable<Integer> data, int expectedSize)272     private ResultAsserter<Iterable<Integer>> sliceResultAsserter(Iterable<Integer> data,
273                                                                   int expectedSize) {
274         return (act, exp, ord, par) -> {
275             if (par & !ord) {
276                 List<Integer> expected = new ArrayList<>();
277                 data.forEach(expected::add);
278 
279                 List<Integer> actual = new ArrayList<>();
280                 act.forEach(actual::add);
281 
282                 assertEquals(actual.size(), expectedSize);
283                 assertTrue(expected.containsAll(actual));
284             }
285             else {
286                 LambdaTestHelpers.assertContents(act, exp);
287             }
288         };
289     }
290 
291     private void testSliceMulti(TestData.OfRef<Integer> data,
292                                 int expectedSize,
293                                 Function<Stream<Integer>, Stream<Integer>> mRef,
294                                 Function<IntStream, IntStream> mInt,
295                                 Function<LongStream, LongStream> mLong,
296                                 Function<DoubleStream, DoubleStream> mDouble) {
297 
298         @SuppressWarnings({ "rawtypes", "unchecked" })
299         Function<Stream<Integer>, Stream<Integer>>[] ms = new Function[4];
300         ms[0] = mRef;
301         ms[1] = s -> mInt.apply(s.mapToInt(e -> e)).mapToObj(e -> e);
302         ms[2] = s -> mLong.apply(s.mapToLong(e -> e)).mapToObj(e -> (int) e);
303         ms[3] = s -> mDouble.apply(s.mapToDouble(e -> e)).mapToObj(e -> (int) e);
304         testSliceMulti(data, expectedSize, ms);
305     }
306 
307     @SafeVarargs
308     private final void testSliceMulti(TestData.OfRef<Integer> data,
309                                       int expectedSize,
310                                       Function<Stream<Integer>, Stream<Integer>>... ms) {
311         for (int i = 0; i < ms.length; i++) {
312             setContext("mIndex", i);
313             Function<Stream<Integer>, Stream<Integer>> m = ms[i];
314             Collection<Integer> sr = withData(data)
315                     .stream(m)
316                     .resultAsserter(sliceResultAsserter(data, expectedSize))
317                     .exercise();
318             assertEquals(sr.size(), expectedSize);
319         }
320     }
321 
322     public void testLimitSort() {
323         List<Integer> l = countTo(100);
324         Collections.reverse(l);
325         exerciseOps(l, s -> s.limit(10).sorted(Comparator.naturalOrder()));
326     }
327 
328     @Test(groups = { "serialization-hostile" })
329     public void testLimitShortCircuit() {
330         for (int l : Arrays.asList(0, 10)) {
331             setContext("l", l);
332             AtomicInteger ai = new AtomicInteger();
333             countTo(100).stream()
334                     .peek(i -> ai.getAndIncrement())
335                     .limit(l).toArray();
336             // For the case of a zero limit, one element will get pushed through the sink chain
337             assertEquals(ai.get(), l, "tee block was called too many times");
338         }
339     }
340 
341     private List<Integer> sizes(int size) {
342         if (size < 4) {
343             return Arrays.asList(0, 1, 2, 3, 4, 6);
344         }
345         else {
346             return Arrays.asList(0, 1, size / 2, size - 1, size, size + 1, 2 * size);
347         }
348     }
349 
350     public void testLimitParallelHugeInput() {
351         for (int n : new int[] {10, 100, 1000, 10000}) {
352             long[] actual = LongStream.range(0, Long.MAX_VALUE)
353                                   .parallel().filter(x -> true) // remove SIZED
354                                   .limit(n).toArray();
355             assertEquals(LongStream.range(0, n).toArray(), actual);
356         }
357     }
358 
359     public void testSliceOpsSpliteratorPreservesSized() {
360         var parSpliterator = IntStream.range(0, 1000).parallel().skip(50).limit(800).spliterator();
361         assertTrue(parSpliterator.hasCharacteristics(Spliterator.SIZED));
362         assertTrue(parSpliterator.hasCharacteristics(Spliterator.SUBSIZED));
363         assertEquals(parSpliterator.getExactSizeIfKnown(), 800);
364         // Original spliterator is split to [0..499] and [500..999] parts
365         // due to skip+limit, we have [50..499] and [500..849]
366         var prefix = parSpliterator.trySplit();
367         assertNotNull(prefix);
368         assertTrue(parSpliterator.hasCharacteristics(Spliterator.SIZED));
369         assertTrue(parSpliterator.hasCharacteristics(Spliterator.SUBSIZED));
370         assertEquals(parSpliterator.getExactSizeIfKnown(), 350);
371         assertTrue(prefix.hasCharacteristics(Spliterator.SIZED));
372         assertTrue(prefix.hasCharacteristics(Spliterator.SUBSIZED));
373         assertEquals(prefix.getExactSizeIfKnown(), 450);
374 
375         var seqSpliterator = IntStream.range(0, 1000).skip(50).limit(800).spliterator();
376         assertTrue(seqSpliterator.hasCharacteristics(Spliterator.SIZED));
377         assertTrue(seqSpliterator.hasCharacteristics(Spliterator.SUBSIZED));
378         assertEquals(seqSpliterator.getExactSizeIfKnown(), 800);
379     }
380 }
381