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