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