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