1 /*
2  * Copyright (c) 2015, 2017, 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 android.platform.test.annotations.LargeTest;
26 
27 import org.openjdk.testlib.java.util.stream.DefaultMethodStreams;
28 import org.openjdk.testlib.java.util.stream.LambdaTestHelpers;
29 import org.openjdk.testlib.java.util.stream.OpTestCase;
30 import org.openjdk.testlib.java.util.stream.StreamTestDataProvider;
31 import org.openjdk.testlib.java.util.stream.TestData;
32 import org.testng.annotations.Test;
33 
34 import java.util.ArrayList;
35 import java.util.Collection;
36 import java.util.HashMap;
37 import java.util.LinkedHashSet;
38 import java.util.List;
39 import java.util.Map;
40 import java.util.Set;
41 import java.util.concurrent.atomic.AtomicBoolean;
42 import java.util.function.Function;
43 import java.util.function.Predicate;
44 import java.util.stream.DoubleStream;
45 import java.util.stream.IntStream;
46 import java.util.stream.LongStream;
47 import java.util.stream.Stream;
48 
49 /*
50  * @test
51  * @bug 8071597 8193856
52  * @run main/timeout=240
53  */
54 @Test
55 public class WhileOpTest extends OpTestCase {
56 
57     @LargeTest
58     @Test(dataProvider = "StreamTestData<Integer>", dataProviderClass = StreamTestDataProvider.class,
59           groups = { "serialization-hostile" })
testTakeWhileOps(String name, TestData.OfRef<Integer> data)60     public void testTakeWhileOps(String name, TestData.OfRef<Integer> data) {
61         for (int size : sizes(data.size())) {
62             setContext("takeWhile", size);
63 
64             testWhileMulti(data,
65                            whileResultAsserter(data, WhileOp.Take, e -> e < size),
66                            s -> s.takeWhile(e -> e < size),
67                            s -> s.takeWhile(e -> e < size),
68                            s -> s.takeWhile(e -> e < size),
69                            s -> s.takeWhile(e -> e < size));
70 
71 
72             testWhileMulti(data,
73                            whileResultAsserter(data, WhileOp.Take, e -> e < size / 2),
74                            s -> s.takeWhile(e -> e < size).takeWhile(e -> e < size / 2),
75                            s -> s.takeWhile(e -> e < size).takeWhile(e -> e < size / 2),
76                            s -> s.takeWhile(e -> e < size).takeWhile(e -> e < size / 2),
77                            s -> s.takeWhile(e -> e < size).takeWhile(e -> e < size / 2));
78         }
79     }
80 
81     @LargeTest
82     @Test(dataProvider = "StreamTestData<Integer>", dataProviderClass = StreamTestDataProvider.class,
83           groups = { "serialization-hostile" })
testDropWhileOps(String name, TestData.OfRef<Integer> data)84     public void testDropWhileOps(String name, TestData.OfRef<Integer> data) {
85         for (int size : sizes(data.size())) {
86             setContext("dropWhile", size);
87 
88             testWhileMulti(data,
89                            whileResultAsserter(data, WhileOp.Drop, e -> e < size),
90                            s -> s.dropWhile(e -> e < size),
91                            s -> s.dropWhile(e -> e < size),
92                            s -> s.dropWhile(e -> e < size),
93                            s -> s.dropWhile(e -> e < size));
94 
95             testWhileMulti(data,
96                            whileResultAsserter(data, WhileOp.Drop, e -> e < size),
97                            s -> s.dropWhile(e -> e < size / 2).dropWhile(e -> e < size),
98                            s -> s.dropWhile(e -> e < size / 2).dropWhile(e -> e < size),
99                            s -> s.dropWhile(e -> e < size / 2).dropWhile(e -> e < size),
100                            s -> s.dropWhile(e -> e < size / 2).dropWhile(e -> e < size));
101         }
102     }
103 
104     @LargeTest
105     @Test(dataProvider = "StreamTestData<Integer>", dataProviderClass = StreamTestDataProvider.class,
106           groups = { "serialization-hostile" })
testDropTakeWhileOps(String name, TestData.OfRef<Integer> data)107     public void testDropTakeWhileOps(String name, TestData.OfRef<Integer> data) {
108         for (int size : sizes(data.size())) {
109             setContext("dropWhile", size);
110 
111             testWhileMulti(data,
112                            whileResultAsserter(data, WhileOp.Undefined, null),
113                            s -> s.dropWhile(e -> e < size / 2).takeWhile(e -> e < size),
114                            s -> s.dropWhile(e -> e < size / 2).takeWhile(e -> e < size),
115                            s -> s.dropWhile(e -> e < size / 2).takeWhile(e -> e < size),
116                            s -> s.dropWhile(e -> e < size / 2).takeWhile(e -> e < size));
117         }
118     }
119 
120     /**
121      * While operation type to be asserted on
122      */
123     enum WhileOp {
124         /**
125          * The takeWhile operation
126          */
127         Take,
128         /**
129          * The dropWhile operation
130          */
131         Drop,
132         /**
133          * The operation(s) are undefined
134          */
135         Undefined
136     }
137 
138     /**
139      * Create a result asserter for takeWhile or dropWhile operations.
140      * <p>
141      * If the stream pipeline consists of the takeWhile operation
142      * ({@link WhileOp#Take}) or the dropWhile operation ({@link WhileOp#Drop})
143      * then specific assertions can be made on the actual result based on the
144      * input elements, {@code inputData}, and whether those elements match the
145      * predicate, {@code p}, of the operation.
146      * <p>
147      * If the input elements have an encounter order then the actual result
148      * is asserted against the result of operating sequentially on input
149      * elements given the predicate and in accordance with the operation
150      * semantics. (The actual result whether produced sequentially or in
151      * parallel should the same.)
152      * <p>
153      * If the input elements have no encounter order then an actual result
154      * is, for practical purposes, considered non-deterministic.
155      * Consider an input list of lists that contains all possible permutations
156      * of the input elements, and a output list of lists that is the result of
157      * applying the pipeline with the operation sequentially to each input
158      * list.
159      * Any list in the output lists is a valid result. It's not practical to
160      * test in such a manner.
161      * For a takeWhile operation the following assertions can be made if
162      * only some of the input elements match the predicate (i.e. taking will
163      * short-circuit the pipeline):
164      * <ol>
165      * <li>The set of output elements is a subset of the set of matching
166      * input elements</li>
167      * <li>The set of output elements and the set of non-matching input
168      * element are disjoint</li>
169      * </ol>
170      * For a dropWhile operation the following assertions can be made:
171      * <ol>
172      * <li>The set of non-matching input elements is a subset of the set of
173      * output elements</li>
174      * <li>The set of matching output elements is a subset of the set of
175      * matching input elements</li>
176      * </ol>
177      *
178      * @param inputData the elements input into the stream pipeline
179      * @param op the operation of the stream pipeline, one of takeWhile,
180      * dropWhile, or an undefined set of operations (possibly including
181      * two or more takeWhile and/or dropWhile operations, or because
182      * the predicate is not stateless).
183      * @param p the stateless predicate applied to the operation, ignored if
184      * the
185      * operation is {@link WhileOp#Undefined}.
186      * @param <T> the type of elements
187      * @return a result asserter
188      */
whileResultAsserter(Iterable<T> inputData, WhileOp op, Predicate<? super T> p)189     private <T> ResultAsserter<Iterable<T>> whileResultAsserter(Iterable<T> inputData,
190                                                                 WhileOp op,
191                                                                 Predicate<? super T> p) {
192         return (act, exp, ord, par) -> {
193             if (par & !ord) {
194                 List<T> input = new ArrayList<>();
195                 inputData.forEach(input::add);
196 
197                 List<T> output = new ArrayList<>();
198                 act.forEach(output::add);
199 
200                 if (op == WhileOp.Take) {
201                     List<T> matchingInput = new ArrayList<>();
202                     List<T> nonMatchingInput = new ArrayList<>();
203                     input.forEach(t -> {
204                         if (p.test(t))
205                             matchingInput.add(t);
206                         else
207                             nonMatchingInput.add(t);
208                     });
209 
210                     // If some, not all, elements are taken
211                     if (matchingInput.size() < input.size()) {
212                         assertTrue(output.size() <= matchingInput.size(),
213                                    "Output is larger than the matching input");
214 
215                         // The output must be a subset of the matching input
216                         assertTrue(matchingInput.containsAll(output),
217                                    "Output is not a subset of the matching input");
218 
219                         // The output must not contain any non matching elements
220                         for (T nonMatching : nonMatchingInput) {
221                             assertFalse(output.contains(nonMatching),
222                                         "Output and non-matching input are not disjoint");
223                         }
224                     }
225                 }
226                 else if (op == WhileOp.Drop) {
227                     List<T> matchingInput = new ArrayList<>();
228                     List<T> nonMatchingInput = new ArrayList<>();
229                     input.forEach(t -> {
230                         if (p.test(t))
231                             matchingInput.add(t);
232                         else
233                             nonMatchingInput.add(t);
234                     });
235 
236                     // The non matching input must be a subset of output
237                     assertTrue(output.containsAll(nonMatchingInput),
238                                "Non-matching input is not a subset of the output");
239 
240                     // The matching output must be a subset of the matching input
241                     List<T> matchingOutput = new ArrayList<>();
242                     output.forEach(i -> {
243                         if (p.test(i))
244                             matchingOutput.add(i);
245                     });
246                     assertTrue(matchingInput.containsAll(matchingOutput),
247                                "Matching output is not a subset of matching input");
248                 }
249 
250                 // Note: if there is a combination of takeWhile and dropWhile then specific
251                 // assertions cannot be performed.
252                 // All that can be reliably asserted is the output is a subset of the input
253 
254                 assertTrue(input.containsAll(output));
255             }
256             else {
257                 // For specific operations derive expected result from the input
258                 if (op == WhileOp.Take) {
259                     List<T> takeInput = new ArrayList<>();
260                     for (T t : inputData) {
261                         if (p.test(t))
262                             takeInput.add(t);
263                         else
264                             break;
265                     }
266 
267                     LambdaTestHelpers.assertContents(act, takeInput);
268                 }
269                 else if (op == WhileOp.Drop) {
270                     List<T> dropInput = new ArrayList<>();
271                     for (T t : inputData) {
272                         if (dropInput.size() > 0 || !p.test(t))
273                             dropInput.add(t);
274                     }
275 
276                     LambdaTestHelpers.assertContents(act, dropInput);
277                 }
278 
279                 LambdaTestHelpers.assertContents(act, exp);
280             }
281         };
282     }
283 
284     private Collection<Integer> sizes(int s) {
285         Set<Integer> sizes = new LinkedHashSet<>();
286 
287         sizes.add(0);
288         sizes.add(1);
289         sizes.add(s / 4);
290         sizes.add(s / 2);
291         sizes.add(3 * s / 4);
292         sizes.add(Math.max(0, s - 1));
293         sizes.add(s);
294         sizes.add(Integer.MAX_VALUE);
295 
296         return sizes;
297     }
298 
299     private void testWhileMulti(TestData.OfRef<Integer> data,
300                                 ResultAsserter<Iterable<Integer>> ra,
301                                 Function<Stream<Integer>, Stream<Integer>> mRef,
302                                 Function<IntStream, IntStream> mInt,
303                                 Function<LongStream, LongStream> mLong,
304                                 Function<DoubleStream, DoubleStream> mDouble) {
305         Map<String, Function<Stream<Integer>, Stream<Integer>>> ms = new HashMap<>();
306         ms.put("Ref", mRef);
307         ms.put("Int", s -> mInt.apply(s.mapToInt(e -> e)).mapToObj(e -> e));
308         ms.put("Long", s -> mLong.apply(s.mapToLong(e -> e)).mapToObj(e -> (int) e));
309         ms.put("Double", s -> mDouble.apply(s.mapToDouble(e -> e)).mapToObj(e -> (int) e));
310         ms.put("Ref using defaults", s -> mRef.apply(DefaultMethodStreams.delegateTo(s)));
311         ms.put("Int using defaults", s -> mInt.apply(DefaultMethodStreams.delegateTo(s.mapToInt(e -> e))).mapToObj(e -> e));
312         ms.put("Long using defaults", s -> mLong.apply(DefaultMethodStreams.delegateTo(s.mapToLong(e -> e))).mapToObj(e -> (int) e));
313         ms.put("Double using defaults", s -> mDouble.apply(DefaultMethodStreams.delegateTo(s.mapToDouble(e -> e))).mapToObj(e -> (int) e));
314 
315         testWhileMulti(data, ra, ms);
316     }
317 
318     private final void testWhileMulti(TestData.OfRef<Integer> data,
319                                       ResultAsserter<Iterable<Integer>> ra,
320                                       Map<String, Function<Stream<Integer>, Stream<Integer>>> ms) {
321         for (Map.Entry<String, Function<Stream<Integer>, Stream<Integer>>> e : ms.entrySet()) {
322             setContext("shape", e.getKey());
323 
324             withData(data)
325                     .stream(e.getValue())
326                     .resultAsserter(ra)
327                     .exercise();
328         }
329     }
330 
331     @Test(groups = { "serialization-hostile" })
332     public void testRefDefaultClose() {
333         AtomicBoolean isClosed = new AtomicBoolean();
334         Stream<Integer> s = Stream.of(1, 2, 3).onClose(() -> isClosed.set(true));
335         try (Stream<Integer> ds = DefaultMethodStreams.delegateTo(s).takeWhile(e -> e < 3)) {
336             ds.count();
337         }
338         assertTrue(isClosed.get());
339     }
340 
341     @Test(groups = { "serialization-hostile" })
342     public void testIntDefaultClose() {
343         AtomicBoolean isClosed = new AtomicBoolean();
344         IntStream s = IntStream.of(1, 2, 3).onClose(() -> isClosed.set(true));
345         try (IntStream ds = DefaultMethodStreams.delegateTo(s).takeWhile(e -> e < 3)) {
346             ds.count();
347         }
348         assertTrue(isClosed.get());
349     }
350 
351     @Test(groups = { "serialization-hostile" })
352     public void testLongDefaultClose() {
353         AtomicBoolean isClosed = new AtomicBoolean();
354         LongStream s = LongStream.of(1, 2, 3).onClose(() -> isClosed.set(true));
355         try (LongStream ds = DefaultMethodStreams.delegateTo(s).takeWhile(e -> e < 3)) {
356             ds.count();
357         }
358         assertTrue(isClosed.get());
359     }
360 
361     @Test(groups = { "serialization-hostile" })
362     public void testDoubleDefaultClose() {
363         AtomicBoolean isClosed = new AtomicBoolean();
364         DoubleStream s = DoubleStream.of(1, 2, 3).onClose(() -> isClosed.set(true));
365         try (DoubleStream ds = DefaultMethodStreams.delegateTo(s).takeWhile(e -> e < 3)) {
366             ds.count();
367         }
368         assertTrue(isClosed.get());
369     }
370 
371     @Test(groups = { "serialization-hostile" })
372     public void testFlatMapThenTake() {
373         TestData.OfRef<Integer> range = TestData.Factory.ofSupplier(
374                 "range", () -> IntStream.range(0, 100).boxed());
375 
376         exerciseOpsMulti(range,
377                          // Reference result
378                          s -> s.takeWhile(e -> e != 50),
379                          // For other results collect into array,
380                          // stream the single array (not the elements),
381                          // then flat map to stream the array elements
382                          s -> Stream.<Integer[]>of(s.toArray(Integer[]::new)).
383                                  flatMap(Stream::of).
384                                  takeWhile(e -> e != 50),
385                          s -> Stream.of(s.mapToInt(e -> e).toArray()).
386                                  flatMapToInt(IntStream::of).
387                                  takeWhile(e -> e != 50).
388                                  mapToObj(e -> e),
389                          s -> Stream.of(s.mapToLong(e -> e).toArray()).
390                                  flatMapToLong(LongStream::of).
391                                  takeWhile(e -> e != 50L).
392                                  mapToObj(e -> (int) e),
393                          s -> Stream.of(s.mapToDouble(e -> e).toArray()).
394                                  flatMapToDouble(DoubleStream::of).
395                                  takeWhile(e -> e != 50.0).
396                                  mapToObj(e -> (int) e)
397                          );
398     }
399 }
400