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