1 /* 2 * Copyright (c) 2012, 2013, 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.*; 26 27 import java.util.ArrayList; 28 import java.util.Arrays; 29 import java.util.Collection; 30 import java.util.Collections; 31 import java.util.Comparator; 32 import java.util.HashMap; 33 import java.util.HashSet; 34 import java.util.Iterator; 35 import java.util.List; 36 import java.util.Map; 37 import java.util.Optional; 38 import java.util.Set; 39 import java.util.StringJoiner; 40 import java.util.TreeMap; 41 import java.util.concurrent.ConcurrentHashMap; 42 import java.util.concurrent.ConcurrentSkipListMap; 43 import java.util.function.BinaryOperator; 44 import java.util.function.Function; 45 import java.util.function.Predicate; 46 import java.util.function.Supplier; 47 import java.util.stream.Collector; 48 import java.util.stream.Collectors; 49 import java.util.stream.Stream; 50 51 import org.testng.annotations.Test; 52 53 import static java.util.stream.Collectors.collectingAndThen; 54 import static java.util.stream.Collectors.groupingBy; 55 import static java.util.stream.Collectors.groupingByConcurrent; 56 import static java.util.stream.Collectors.partitioningBy; 57 import static java.util.stream.Collectors.reducing; 58 import static java.util.stream.Collectors.toCollection; 59 import static java.util.stream.Collectors.toConcurrentMap; 60 import static java.util.stream.Collectors.toList; 61 import static java.util.stream.Collectors.toMap; 62 import static java.util.stream.Collectors.toSet; 63 import static org.openjdk.testlib.java.util.stream.LambdaTestHelpers.assertContents; 64 import static org.openjdk.testlib.java.util.stream.LambdaTestHelpers.assertContentsUnordered; 65 import static org.openjdk.testlib.java.util.stream.LambdaTestHelpers.mDoubler; 66 67 /** 68 * TabulatorsTest 69 * 70 * @author Brian Goetz 71 */ 72 @SuppressWarnings({"rawtypes", "unchecked"}) 73 public class TabulatorsTest extends OpTestCase { 74 75 private static abstract class TabulationAssertion<T, U> { assertValue(U value, Supplier<Stream<T>> source, boolean ordered)76 abstract void assertValue(U value, 77 Supplier<Stream<T>> source, 78 boolean ordered) throws ReflectiveOperationException; 79 } 80 81 @SuppressWarnings({"rawtypes", "unchecked"}) 82 static class GroupedMapAssertion<T, K, V, M extends Map<K, ? extends V>> extends TabulationAssertion<T, M> { 83 private final Class<? extends Map> clazz; 84 private final Function<T, K> classifier; 85 private final TabulationAssertion<T,V> downstream; 86 GroupedMapAssertion(Function<T, K> classifier, Class<? extends Map> clazz, TabulationAssertion<T, V> downstream)87 protected GroupedMapAssertion(Function<T, K> classifier, 88 Class<? extends Map> clazz, 89 TabulationAssertion<T, V> downstream) { 90 this.clazz = clazz; 91 this.classifier = classifier; 92 this.downstream = downstream; 93 } 94 assertValue(M map, Supplier<Stream<T>> source, boolean ordered)95 void assertValue(M map, 96 Supplier<Stream<T>> source, 97 boolean ordered) throws ReflectiveOperationException { 98 if (!clazz.isAssignableFrom(map.getClass())) 99 fail(String.format("Class mismatch in GroupedMapAssertion: %s, %s", clazz, map.getClass())); 100 assertContentsUnordered(map.keySet(), source.get().map(classifier).collect(toSet())); 101 for (Map.Entry<K, ? extends V> entry : map.entrySet()) { 102 K key = entry.getKey(); 103 downstream.assertValue(entry.getValue(), 104 () -> source.get().filter(e -> classifier.apply(e).equals(key)), 105 ordered); 106 } 107 } 108 } 109 110 static class ToMapAssertion<T, K, V, M extends Map<K,V>> extends TabulationAssertion<T, M> { 111 private final Class<? extends Map> clazz; 112 private final Function<T, K> keyFn; 113 private final Function<T, V> valueFn; 114 private final BinaryOperator<V> mergeFn; 115 ToMapAssertion(Function<T, K> keyFn, Function<T, V> valueFn, BinaryOperator<V> mergeFn, Class<? extends Map> clazz)116 ToMapAssertion(Function<T, K> keyFn, 117 Function<T, V> valueFn, 118 BinaryOperator<V> mergeFn, 119 Class<? extends Map> clazz) { 120 this.clazz = clazz; 121 this.keyFn = keyFn; 122 this.valueFn = valueFn; 123 this.mergeFn = mergeFn; 124 } 125 126 @Override assertValue(M map, Supplier<Stream<T>> source, boolean ordered)127 void assertValue(M map, Supplier<Stream<T>> source, boolean ordered) throws ReflectiveOperationException { 128 Set<K> uniqueKeys = source.get().map(keyFn).collect(toSet()); 129 assertTrue(clazz.isAssignableFrom(map.getClass())); 130 assertEquals(uniqueKeys, map.keySet()); 131 source.get().forEach(t -> { 132 K key = keyFn.apply(t); 133 V v = source.get() 134 .filter(e -> key.equals(keyFn.apply(e))) 135 .map(valueFn) 136 .reduce(mergeFn) 137 .get(); 138 assertEquals(map.get(key), v); 139 }); 140 } 141 } 142 143 static class PartitionAssertion<T, D> extends TabulationAssertion<T, Map<Boolean,D>> { 144 private final Predicate<T> predicate; 145 private final TabulationAssertion<T,D> downstream; 146 PartitionAssertion(Predicate<T> predicate, TabulationAssertion<T, D> downstream)147 protected PartitionAssertion(Predicate<T> predicate, 148 TabulationAssertion<T, D> downstream) { 149 this.predicate = predicate; 150 this.downstream = downstream; 151 } 152 assertValue(Map<Boolean, D> map, Supplier<Stream<T>> source, boolean ordered)153 void assertValue(Map<Boolean, D> map, 154 Supplier<Stream<T>> source, 155 boolean ordered) throws ReflectiveOperationException { 156 if (!Map.class.isAssignableFrom(map.getClass())) 157 fail(String.format("Class mismatch in PartitionAssertion: %s", map.getClass())); 158 assertEquals(2, map.size()); 159 downstream.assertValue(map.get(true), () -> source.get().filter(predicate), ordered); 160 downstream.assertValue(map.get(false), () -> source.get().filter(predicate.negate()), ordered); 161 } 162 } 163 164 @SuppressWarnings({"rawtypes", "unchecked"}) 165 static class ListAssertion<T> extends TabulationAssertion<T, List<T>> { 166 @Override assertValue(List<T> value, Supplier<Stream<T>> source, boolean ordered)167 void assertValue(List<T> value, Supplier<Stream<T>> source, boolean ordered) 168 throws ReflectiveOperationException { 169 if (!List.class.isAssignableFrom(value.getClass())) 170 fail(String.format("Class mismatch in ListAssertion: %s", value.getClass())); 171 Stream<T> stream = source.get(); 172 List<T> result = new ArrayList<>(); 173 for (Iterator<T> it = stream.iterator(); it.hasNext(); ) // avoid capturing result::add 174 result.add(it.next()); 175 if (StreamOpFlagTestHelper.isStreamOrdered(stream) && ordered) 176 assertContents(value, result); 177 else 178 assertContentsUnordered(value, result); 179 } 180 } 181 182 @SuppressWarnings({"rawtypes", "unchecked"}) 183 static class CollectionAssertion<T> extends TabulationAssertion<T, Collection<T>> { 184 private final Class<? extends Collection> clazz; 185 private final boolean targetOrdered; 186 CollectionAssertion(Class<? extends Collection> clazz, boolean targetOrdered)187 protected CollectionAssertion(Class<? extends Collection> clazz, boolean targetOrdered) { 188 this.clazz = clazz; 189 this.targetOrdered = targetOrdered; 190 } 191 192 @Override assertValue(Collection<T> value, Supplier<Stream<T>> source, boolean ordered)193 void assertValue(Collection<T> value, Supplier<Stream<T>> source, boolean ordered) 194 throws ReflectiveOperationException { 195 if (!clazz.isAssignableFrom(value.getClass())) 196 fail(String.format("Class mismatch in CollectionAssertion: %s, %s", clazz, value.getClass())); 197 Stream<T> stream = source.get(); 198 Collection<T> result = clazz.newInstance(); 199 for (Iterator<T> it = stream.iterator(); it.hasNext(); ) // avoid capturing result::add 200 result.add(it.next()); 201 if (StreamOpFlagTestHelper.isStreamOrdered(stream) && targetOrdered && ordered) 202 assertContents(value, result); 203 else 204 assertContentsUnordered(value, result); 205 } 206 } 207 208 static class ReduceAssertion<T, U> extends TabulationAssertion<T, U> { 209 private final U identity; 210 private final Function<T, U> mapper; 211 private final BinaryOperator<U> reducer; 212 ReduceAssertion(U identity, Function<T, U> mapper, BinaryOperator<U> reducer)213 ReduceAssertion(U identity, Function<T, U> mapper, BinaryOperator<U> reducer) { 214 this.identity = identity; 215 this.mapper = mapper; 216 this.reducer = reducer; 217 } 218 219 @Override assertValue(U value, Supplier<Stream<T>> source, boolean ordered)220 void assertValue(U value, Supplier<Stream<T>> source, boolean ordered) 221 throws ReflectiveOperationException { 222 Optional<U> reduced = source.get().map(mapper).reduce(reducer); 223 if (value == null) 224 assertTrue(!reduced.isPresent()); 225 else if (!reduced.isPresent()) { 226 assertEquals(value, identity); 227 } 228 else { 229 assertEquals(value, reduced.get()); 230 } 231 } 232 } 233 mapTabulationAsserter(boolean ordered)234 private <T> ResultAsserter<T> mapTabulationAsserter(boolean ordered) { 235 return (act, exp, ord, par) -> { 236 if (par && (!ordered || !ord)) { 237 TabulatorsTest.nestedMapEqualityAssertion(act, exp); 238 } 239 else { 240 LambdaTestHelpers.assertContentsEqual(act, exp); 241 } 242 }; 243 } 244 245 private<T, M extends Map> 246 void exerciseMapTabulation(TestData<T, Stream<T>> data, 247 Collector<T, ?, ? extends M> collector, 248 TabulationAssertion<T, M> assertion) 249 throws ReflectiveOperationException { 250 boolean ordered = !collector.characteristics().contains(Collector.Characteristics.UNORDERED); 251 252 M m = withData(data) 253 .terminal(s -> s.collect(collector)) 254 .resultAsserter(mapTabulationAsserter(ordered)) 255 .exercise(); 256 assertion.assertValue(m, () -> data.stream(), ordered); 257 258 m = withData(data) 259 .terminal(s -> s.unordered().collect(collector)) 260 .resultAsserter(mapTabulationAsserter(ordered)) 261 .exercise(); 262 assertion.assertValue(m, () -> data.stream(), false); 263 } 264 265 private static void nestedMapEqualityAssertion(Object o1, Object o2) { 266 if (o1 instanceof Map) { 267 Map m1 = (Map) o1; 268 Map m2 = (Map) o2; 269 assertContentsUnordered(m1.keySet(), m2.keySet()); 270 for (Object k : m1.keySet()) 271 nestedMapEqualityAssertion(m1.get(k), m2.get(k)); 272 } 273 else if (o1 instanceof Collection) { 274 assertContentsUnordered(((Collection) o1), ((Collection) o2)); 275 } 276 else 277 assertEquals(o1, o2); 278 } 279 280 private<T, R> void assertCollect(TestData.OfRef<T> data, 281 Collector<T, ?, R> collector, 282 Function<Stream<T>, R> streamReduction) { 283 R check = streamReduction.apply(data.stream()); 284 withData(data).terminal(s -> s.collect(collector)).expectedResult(check).exercise(); 285 } 286 287 @Test(dataProvider = "StreamTestData<Integer>", dataProviderClass = StreamTestDataProvider.class) 288 public void testReduce(String name, TestData.OfRef<Integer> data) throws ReflectiveOperationException { 289 assertCollect(data, Collectors.reducing(0, Integer::sum), 290 s -> s.reduce(0, Integer::sum)); 291 assertCollect(data, Collectors.reducing(Integer.MAX_VALUE, Integer::min), 292 s -> s.min(Integer::compare).orElse(Integer.MAX_VALUE)); 293 assertCollect(data, Collectors.reducing(Integer.MIN_VALUE, Integer::max), 294 s -> s.max(Integer::compare).orElse(Integer.MIN_VALUE)); 295 296 assertCollect(data, Collectors.reducing(Integer::sum), 297 s -> s.reduce(Integer::sum)); 298 assertCollect(data, Collectors.minBy(Comparator.naturalOrder()), 299 s -> s.min(Integer::compare)); 300 assertCollect(data, Collectors.maxBy(Comparator.naturalOrder()), 301 s -> s.max(Integer::compare)); 302 303 assertCollect(data, Collectors.reducing(0, x -> x*2, Integer::sum), 304 s -> s.map(x -> x*2).reduce(0, Integer::sum)); 305 306 assertCollect(data, Collectors.summingLong(x -> x * 2L), 307 s -> s.map(x -> x*2L).reduce(0L, Long::sum)); 308 assertCollect(data, Collectors.summingInt(x -> x * 2), 309 s -> s.map(x -> x*2).reduce(0, Integer::sum)); 310 assertCollect(data, Collectors.summingDouble(x -> x * 2.0d), 311 s -> s.map(x -> x * 2.0d).reduce(0.0d, Double::sum)); 312 313 assertCollect(data, Collectors.averagingInt(x -> x * 2), 314 s -> s.mapToInt(x -> x * 2).average().orElse(0)); 315 assertCollect(data, Collectors.averagingLong(x -> x * 2), 316 s -> s.mapToLong(x -> x * 2).average().orElse(0)); 317 assertCollect(data, Collectors.averagingDouble(x -> x * 2), 318 s -> s.mapToDouble(x -> x * 2).average().orElse(0)); 319 320 // Test explicit Collector.of 321 Collector<Integer, long[], Double> avg2xint = Collector.of(() -> new long[2], 322 (a, b) -> { 323 a[0] += b * 2; 324 a[1]++; 325 }, 326 (a, b) -> { 327 a[0] += b[0]; 328 a[1] += b[1]; 329 return a; 330 }, 331 a -> a[1] == 0 ? 0.0d : (double) a[0] / a[1]); 332 assertCollect(data, avg2xint, 333 s -> s.mapToInt(x -> x * 2).average().orElse(0)); 334 } 335 336 @Test(dataProvider = "StreamTestData<Integer>", dataProviderClass = StreamTestDataProvider.class) 337 public void testJoin(String name, TestData.OfRef<Integer> data) throws ReflectiveOperationException { 338 withData(data) 339 .terminal(s -> s.map(Object::toString).collect(Collectors.joining())) 340 .expectedResult(join(data, "")) 341 .exercise(); 342 343 Collector<String, StringBuilder, String> likeJoining = Collector.of(StringBuilder::new, StringBuilder::append, (sb1, sb2) -> sb1.append(sb2.toString()), StringBuilder::toString); 344 withData(data) 345 .terminal(s -> s.map(Object::toString).collect(likeJoining)) 346 .expectedResult(join(data, "")) 347 .exercise(); 348 349 withData(data) 350 .terminal(s -> s.map(Object::toString).collect(Collectors.joining(","))) 351 .expectedResult(join(data, ",")) 352 .exercise(); 353 354 withData(data) 355 .terminal(s -> s.map(Object::toString).collect(Collectors.joining(",", "[", "]"))) 356 .expectedResult("[" + join(data, ",") + "]") 357 .exercise(); 358 359 withData(data) 360 .terminal(s -> s.map(Object::toString) 361 .collect(StringBuilder::new, StringBuilder::append, StringBuilder::append) 362 .toString()) 363 .expectedResult(join(data, "")) 364 .exercise(); 365 366 withData(data) 367 .terminal(s -> s.map(Object::toString) 368 .collect(() -> new StringJoiner(","), 369 (sj, cs) -> sj.add(cs), 370 (j1, j2) -> j1.merge(j2)) 371 .toString()) 372 .expectedResult(join(data, ",")) 373 .exercise(); 374 375 withData(data) 376 .terminal(s -> s.map(Object::toString) 377 .collect(() -> new StringJoiner(",", "[", "]"), 378 (sj, cs) -> sj.add(cs), 379 (j1, j2) -> j1.merge(j2)) 380 .toString()) 381 .expectedResult("[" + join(data, ",") + "]") 382 .exercise(); 383 } 384 385 private<T> String join(TestData.OfRef<T> data, String delim) { 386 StringBuilder sb = new StringBuilder(); 387 boolean first = true; 388 for (T i : data) { 389 if (!first) 390 sb.append(delim); 391 sb.append(i.toString()); 392 first = false; 393 } 394 return sb.toString(); 395 } 396 397 @Test(dataProvider = "StreamTestData<Integer>", dataProviderClass = StreamTestDataProvider.class) 398 public void testSimpleToMap(String name, TestData.OfRef<Integer> data) throws ReflectiveOperationException { 399 Function<Integer, Integer> keyFn = i -> i * 2; 400 Function<Integer, Integer> valueFn = i -> i * 4; 401 402 List<Integer> dataAsList = Arrays.asList(data.stream().toArray(Integer[]::new)); 403 Set<Integer> dataAsSet = new HashSet<>(dataAsList); 404 405 BinaryOperator<Integer> sum = Integer::sum; 406 for (BinaryOperator<Integer> op : Arrays.asList((u, v) -> u, 407 (u, v) -> v, 408 sum)) { 409 try { 410 exerciseMapTabulation(data, toMap(keyFn, valueFn), 411 new ToMapAssertion<>(keyFn, valueFn, op, HashMap.class)); 412 if (dataAsList.size() != dataAsSet.size()) 413 fail("Expected ISE on input with duplicates"); 414 } 415 catch (IllegalStateException e) { 416 if (dataAsList.size() == dataAsSet.size()) 417 fail("Expected no ISE on input without duplicates"); 418 } 419 420 exerciseMapTabulation(data, toMap(keyFn, valueFn, op), 421 new ToMapAssertion<>(keyFn, valueFn, op, HashMap.class)); 422 423 exerciseMapTabulation(data, toMap(keyFn, valueFn, op, TreeMap::new), 424 new ToMapAssertion<>(keyFn, valueFn, op, TreeMap.class)); 425 } 426 427 // For concurrent maps, only use commutative merge functions 428 try { 429 exerciseMapTabulation(data, toConcurrentMap(keyFn, valueFn), 430 new ToMapAssertion<>(keyFn, valueFn, sum, ConcurrentHashMap.class)); 431 if (dataAsList.size() != dataAsSet.size()) 432 fail("Expected ISE on input with duplicates"); 433 } 434 catch (IllegalStateException e) { 435 if (dataAsList.size() == dataAsSet.size()) 436 fail("Expected no ISE on input without duplicates"); 437 } 438 439 exerciseMapTabulation(data, toConcurrentMap(keyFn, valueFn, sum), 440 new ToMapAssertion<>(keyFn, valueFn, sum, ConcurrentHashMap.class)); 441 442 exerciseMapTabulation(data, toConcurrentMap(keyFn, valueFn, sum, ConcurrentSkipListMap::new), 443 new ToMapAssertion<>(keyFn, valueFn, sum, ConcurrentSkipListMap.class)); 444 } 445 446 @Test(dataProvider = "StreamTestData<Integer>", dataProviderClass = StreamTestDataProvider.class) 447 public void testSimpleGroupBy(String name, TestData.OfRef<Integer> data) throws ReflectiveOperationException { 448 Function<Integer, Integer> classifier = i -> i % 3; 449 450 // Single-level groupBy 451 exerciseMapTabulation(data, groupingBy(classifier), 452 new GroupedMapAssertion<>(classifier, HashMap.class, 453 new ListAssertion<>())); 454 exerciseMapTabulation(data, groupingByConcurrent(classifier), 455 new GroupedMapAssertion<>(classifier, ConcurrentHashMap.class, 456 new ListAssertion<>())); 457 458 // With explicit constructors 459 exerciseMapTabulation(data, 460 groupingBy(classifier, TreeMap::new, toCollection(HashSet::new)), 461 new GroupedMapAssertion<>(classifier, TreeMap.class, 462 new CollectionAssertion<Integer>(HashSet.class, false))); 463 exerciseMapTabulation(data, 464 groupingByConcurrent(classifier, ConcurrentSkipListMap::new, 465 toCollection(HashSet::new)), 466 new GroupedMapAssertion<>(classifier, ConcurrentSkipListMap.class, 467 new CollectionAssertion<Integer>(HashSet.class, false))); 468 } 469 470 @Test(dataProvider = "StreamTestData<Integer>", dataProviderClass = StreamTestDataProvider.class) 471 public void testTwoLevelGroupBy(String name, TestData.OfRef<Integer> data) throws ReflectiveOperationException { 472 Function<Integer, Integer> classifier = i -> i % 6; 473 Function<Integer, Integer> classifier2 = i -> i % 23; 474 475 // Two-level groupBy 476 exerciseMapTabulation(data, 477 groupingBy(classifier, groupingBy(classifier2)), 478 new GroupedMapAssertion<>(classifier, HashMap.class, 479 new GroupedMapAssertion<>(classifier2, HashMap.class, 480 new ListAssertion<>()))); 481 // with concurrent as upstream 482 exerciseMapTabulation(data, 483 groupingByConcurrent(classifier, groupingBy(classifier2)), 484 new GroupedMapAssertion<>(classifier, ConcurrentHashMap.class, 485 new GroupedMapAssertion<>(classifier2, HashMap.class, 486 new ListAssertion<>()))); 487 // with concurrent as downstream 488 exerciseMapTabulation(data, 489 groupingBy(classifier, groupingByConcurrent(classifier2)), 490 new GroupedMapAssertion<>(classifier, HashMap.class, 491 new GroupedMapAssertion<>(classifier2, ConcurrentHashMap.class, 492 new ListAssertion<>()))); 493 // with concurrent as upstream and downstream 494 exerciseMapTabulation(data, 495 groupingByConcurrent(classifier, groupingByConcurrent(classifier2)), 496 new GroupedMapAssertion<>(classifier, ConcurrentHashMap.class, 497 new GroupedMapAssertion<>(classifier2, ConcurrentHashMap.class, 498 new ListAssertion<>()))); 499 500 // With explicit constructors 501 exerciseMapTabulation(data, 502 groupingBy(classifier, TreeMap::new, groupingBy(classifier2, TreeMap::new, toCollection(HashSet::new))), 503 new GroupedMapAssertion<>(classifier, TreeMap.class, 504 new GroupedMapAssertion<>(classifier2, TreeMap.class, 505 new CollectionAssertion<Integer>(HashSet.class, false)))); 506 // with concurrent as upstream 507 exerciseMapTabulation(data, 508 groupingByConcurrent(classifier, ConcurrentSkipListMap::new, groupingBy(classifier2, TreeMap::new, toList())), 509 new GroupedMapAssertion<>(classifier, ConcurrentSkipListMap.class, 510 new GroupedMapAssertion<>(classifier2, TreeMap.class, 511 new ListAssertion<>()))); 512 // with concurrent as downstream 513 exerciseMapTabulation(data, 514 groupingBy(classifier, TreeMap::new, groupingByConcurrent(classifier2, ConcurrentSkipListMap::new, toList())), 515 new GroupedMapAssertion<>(classifier, TreeMap.class, 516 new GroupedMapAssertion<>(classifier2, ConcurrentSkipListMap.class, 517 new ListAssertion<>()))); 518 // with concurrent as upstream and downstream 519 exerciseMapTabulation(data, 520 groupingByConcurrent(classifier, ConcurrentSkipListMap::new, groupingByConcurrent(classifier2, ConcurrentSkipListMap::new, toList())), 521 new GroupedMapAssertion<>(classifier, ConcurrentSkipListMap.class, 522 new GroupedMapAssertion<>(classifier2, ConcurrentSkipListMap.class, 523 new ListAssertion<>()))); 524 } 525 526 @Test(dataProvider = "StreamTestData<Integer>", dataProviderClass = StreamTestDataProvider.class) 527 public void testGroupedReduce(String name, TestData.OfRef<Integer> data) throws ReflectiveOperationException { 528 Function<Integer, Integer> classifier = i -> i % 3; 529 530 // Single-level simple reduce 531 exerciseMapTabulation(data, 532 groupingBy(classifier, reducing(0, Integer::sum)), 533 new GroupedMapAssertion<>(classifier, HashMap.class, 534 new ReduceAssertion<>(0, LambdaTestHelpers.identity(), Integer::sum))); 535 // with concurrent 536 exerciseMapTabulation(data, 537 groupingByConcurrent(classifier, reducing(0, Integer::sum)), 538 new GroupedMapAssertion<>(classifier, ConcurrentHashMap.class, 539 new ReduceAssertion<>(0, LambdaTestHelpers.identity(), Integer::sum))); 540 541 // With explicit constructors 542 exerciseMapTabulation(data, 543 groupingBy(classifier, TreeMap::new, reducing(0, Integer::sum)), 544 new GroupedMapAssertion<>(classifier, TreeMap.class, 545 new ReduceAssertion<>(0, LambdaTestHelpers.identity(), Integer::sum))); 546 // with concurrent 547 exerciseMapTabulation(data, 548 groupingByConcurrent(classifier, ConcurrentSkipListMap::new, reducing(0, Integer::sum)), 549 new GroupedMapAssertion<>(classifier, ConcurrentSkipListMap.class, 550 new ReduceAssertion<>(0, LambdaTestHelpers.identity(), Integer::sum))); 551 552 // Single-level map-reduce 553 exerciseMapTabulation(data, 554 groupingBy(classifier, reducing(0, mDoubler, Integer::sum)), 555 new GroupedMapAssertion<>(classifier, HashMap.class, 556 new ReduceAssertion<>(0, mDoubler, Integer::sum))); 557 // with concurrent 558 exerciseMapTabulation(data, 559 groupingByConcurrent(classifier, reducing(0, mDoubler, Integer::sum)), 560 new GroupedMapAssertion<>(classifier, ConcurrentHashMap.class, 561 new ReduceAssertion<>(0, mDoubler, Integer::sum))); 562 563 // With explicit constructors 564 exerciseMapTabulation(data, 565 groupingBy(classifier, TreeMap::new, reducing(0, mDoubler, Integer::sum)), 566 new GroupedMapAssertion<>(classifier, TreeMap.class, 567 new ReduceAssertion<>(0, mDoubler, Integer::sum))); 568 // with concurrent 569 exerciseMapTabulation(data, 570 groupingByConcurrent(classifier, ConcurrentSkipListMap::new, reducing(0, mDoubler, Integer::sum)), 571 new GroupedMapAssertion<>(classifier, ConcurrentSkipListMap.class, 572 new ReduceAssertion<>(0, mDoubler, Integer::sum))); 573 } 574 575 @Test(dataProvider = "StreamTestData<Integer>", dataProviderClass = StreamTestDataProvider.class) 576 public void testSimplePartition(String name, TestData.OfRef<Integer> data) throws ReflectiveOperationException { 577 Predicate<Integer> classifier = i -> i % 3 == 0; 578 579 // Single-level partition to downstream List 580 exerciseMapTabulation(data, 581 partitioningBy(classifier), 582 new PartitionAssertion<>(classifier, new ListAssertion<>())); 583 exerciseMapTabulation(data, 584 partitioningBy(classifier, toList()), 585 new PartitionAssertion<>(classifier, new ListAssertion<>())); 586 } 587 588 @Test(dataProvider = "StreamTestData<Integer>", dataProviderClass = StreamTestDataProvider.class) 589 public void testTwoLevelPartition(String name, TestData.OfRef<Integer> data) throws ReflectiveOperationException { 590 Predicate<Integer> classifier = i -> i % 3 == 0; 591 Predicate<Integer> classifier2 = i -> i % 7 == 0; 592 593 // Two level partition 594 exerciseMapTabulation(data, 595 partitioningBy(classifier, partitioningBy(classifier2)), 596 new PartitionAssertion<>(classifier, 597 new PartitionAssertion(classifier2, new ListAssertion<>()))); 598 599 // Two level partition with reduce 600 exerciseMapTabulation(data, 601 partitioningBy(classifier, reducing(0, Integer::sum)), 602 new PartitionAssertion<>(classifier, 603 new ReduceAssertion<>(0, LambdaTestHelpers.identity(), Integer::sum))); 604 } 605 606 @Test(dataProvider = "StreamTestData<Integer>", dataProviderClass = StreamTestDataProvider.class) 607 public void testComposeFinisher(String name, TestData.OfRef<Integer> data) throws ReflectiveOperationException { 608 List<Integer> asList = exerciseTerminalOps(data, s -> s.collect(toList())); 609 // Android-changed: Added a cast to workaround an ECJ bug. http://b/33371837 610 List<Integer> asImmutableList = exerciseTerminalOps(data, s -> (List<Integer>) s.collect(collectingAndThen(toList(), Collections::unmodifiableList))); 611 assertEquals(asList, asImmutableList); 612 try { 613 asImmutableList.add(0); 614 fail("Expecting immutable result"); 615 } 616 catch (UnsupportedOperationException ignored) { } 617 } 618 619 } 620