blob: bfafad0f9906974a70714e4b0d1b803c36a04606 [file] [log] [blame]
/*
* Copyright 2016 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.gwt.emultest.java8.util.stream;
import static java.util.stream.Collectors.averagingDouble;
import static java.util.stream.Collectors.averagingInt;
import static java.util.stream.Collectors.averagingLong;
import static java.util.stream.Collectors.collectingAndThen;
import static java.util.stream.Collectors.counting;
import static java.util.stream.Collectors.groupingBy;
import static java.util.stream.Collectors.joining;
import static java.util.stream.Collectors.mapping;
import static java.util.stream.Collectors.maxBy;
import static java.util.stream.Collectors.minBy;
import static java.util.stream.Collectors.partitioningBy;
import static java.util.stream.Collectors.summarizingDouble;
import static java.util.stream.Collectors.summarizingInt;
import static java.util.stream.Collectors.summarizingLong;
import static java.util.stream.Collectors.summingDouble;
import static java.util.stream.Collectors.summingInt;
import static java.util.stream.Collectors.summingLong;
import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toMap;
import static java.util.stream.Collectors.toSet;
import com.google.gwt.emultest.java.util.EmulTestBase;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.DoubleSummaryStatistics;
import java.util.HashMap;
import java.util.HashSet;
import java.util.IntSummaryStatistics;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.LongSummaryStatistics;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.BiPredicate;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collector;
/**
* Tests {@link java.util.stream.Collectors}.
* <p />
* Methods that are presently only tested indirectly:
* <ul>
* <li>reducing: counting, minBy/maxBy use this</li>
* <li>toCollection tested by toList (toSet now uses its own impl)</li>
* </ul>
*/
public class CollectorsTest extends EmulTestBase {
public void testAveragingDouble() {
Collector<Double, ?, Double> c = averagingDouble(Double::doubleValue);
applyItems((4.0 + 8.0) / 2.0, c, 4.0, 8.0);
assertZeroItemsCollectedAs(0D, c);
assertSingleItemCollectedAs(5D, c, 5D);
}
public void testAveragingInt() {
Collector<Integer, ?, Double> c = averagingInt(Integer::intValue);
applyItems((4.0 + 8.0) / 2.0, c, 4, 8);
assertZeroItemsCollectedAs(0D, c);
assertSingleItemCollectedAs(5D, c, 5);
}
public void testAveragingLong() {
Collector<Long, ?, Double> c = averagingLong(Long::longValue);
applyItems((4.0 + 8.0) / 2.0, c, 4L, 8L);
assertZeroItemsCollectedAs(0D, c);
assertSingleItemCollectedAs(5D, c, 5L);
}
public void testCollectingAndThen() {
Collector<Object, ?, List<Object>> listIdentityCollector =
collectingAndThen(toList(), Function.identity());
// same test as toList():
// same items (allow dups)
applyItems(
Arrays.asList("a", "a"),
listIdentityCollector,
"a", "a"
);
// ordered
applyItems(
Arrays.asList("a", "b"),
listIdentityCollector,
"a", "b"
);
assertZeroItemsCollectedAs(Collections.emptyList(), listIdentityCollector);
assertSingleItemCollectedAs(Collections.singletonList("a"), listIdentityCollector, "a");
Collector<Object, ?, Integer> uglyCount = collectingAndThen(toList(), List::size);
// (nearly) same test as counting():
applyItems(2, uglyCount, "1", new Object());
assertZeroItemsCollectedAs(0, uglyCount);
assertSingleItemCollectedAs(1, uglyCount, new Object());
}
public void testCounting() {
Collector<Object, ?, Long> c = counting();
applyItems(2L, c, "1", new Object());
assertZeroItemsCollectedAs(0L, c);
assertSingleItemCollectedAs(1L, c, new Object());
}
public void testGroupingBy() {
Collector<String, ?, Map<String, List<String>>> c1 = groupingBy(Function.identity());
Map<String, List<String>> mapOfLists = new HashMap<>();
mapOfLists.put("a", Arrays.asList("a", "a"));
applyItems(mapOfLists, c1, "a", "a");
mapOfLists.clear();
mapOfLists.put("a", Collections.singletonList("a"));
mapOfLists.put("b", Collections.singletonList("b"));
applyItems(mapOfLists, c1, "a", "b");
assertZeroItemsCollectedAs(Collections.emptyMap(), c1);
assertSingleItemCollectedAs(
Collections.singletonMap("a", Collections.singletonList("a")), c1, "a");
Collector<String, ?, LinkedHashMap<String, Set<String>>> c2 =
groupingBy(Function.identity(), LinkedHashMap::new, toSet());
LinkedHashMap<String, Set<String>> linkedMapOfSets = new LinkedHashMap<>();
linkedMapOfSets.put("a", Collections.singleton("a"));
applyItems(linkedMapOfSets, c2, "a", "a");
linkedMapOfSets.clear();
linkedMapOfSets.put("a", Collections.singleton("a"));
linkedMapOfSets.put("b", Collections.singleton("b"));
applyItems(linkedMapOfSets, c2, "a", "b");
// check to make sure we actually get the linked results, and that they are ordered how we want
// them
LinkedHashMap<String, Set<String>> out = applyItemsWithoutSplitting(c2, "a", "b");
assertEquals(Arrays.asList("a", "b"), new ArrayList<>(out.keySet()));
out = applyItemsWithoutSplitting(c2, "b", "a");
assertEquals(Arrays.asList("b", "a"), new ArrayList<>(out.keySet()));
assertZeroItemsCollectedAs(new LinkedHashMap<>(), c2);
linkedMapOfSets.clear();
linkedMapOfSets.put("a", Collections.singleton("a"));
assertSingleItemCollectedAs(linkedMapOfSets, c2, "a");
}
public void testJoining() {
Collector<CharSequence, ?, String> c = joining();
applyItems("ab", c, "a", "b");
applyItems("a,", c, "a", ",");
applyItems("", c, "", "");
assertZeroItemsCollectedAs("", c);
assertSingleItemCollectedAs("a", c, "a");
assertSingleItemCollectedAs("", c, "");
c = joining(",");
applyItems("a,b", c, "a", "b");
applyItems("a,,", c, "a", ",");
applyItems(",", c, "", "");
assertZeroItemsCollectedAs("", c);
assertSingleItemCollectedAs("a", c, "a");
assertSingleItemCollectedAs("", c, "");
c = joining("-", "{", "}");
applyItems("{a-b}", c, "a", "b");
assertZeroItemsCollectedAs("{}", c);
assertSingleItemCollectedAs("{a}", c, "a");
assertSingleItemCollectedAs("{}", c, "");
}
public void testMapping() {
Collector<String, ?, List<String>> identityMapping = mapping(Function.identity(), toList());
// same test as toList():
// same items (allow dups)
applyItems(
Arrays.asList("a", "a"),
identityMapping,
"a", "a"
);
// ordered
applyItems(
Arrays.asList("a", "b"),
identityMapping,
"a", "b"
);
assertZeroItemsCollectedAs(Collections.emptyList(), identityMapping);
assertSingleItemCollectedAs(Collections.singletonList("a"), identityMapping, "a");
Collector<Integer, ?, List<String>> numberMapping = mapping(s -> "#" + s, toList());
// poke the same tests as list, make sure the mapper is run
applyItems(
Arrays.asList("#1", "#2"),
numberMapping,
1, 2
);
// ordered
applyItems(
Arrays.asList("#1", "#2"),
numberMapping,
1, 2
);
assertZeroItemsCollectedAs(Collections.emptyList(), numberMapping);
assertSingleItemCollectedAs(Collections.singletonList("#10"), numberMapping, 10);
}
public void testMaxBy() {
Collector<String, ?, Optional<String>> c = maxBy(Comparator.naturalOrder());
applyItems(Optional.of("z"), c, "a", "z");
applyItems(Optional.of("z"), c, "z", "a");
assertZeroItemsCollectedAs(Optional.empty(), c);
assertSingleItemCollectedAs(Optional.of("foo"), c, "foo");
}
public void testMinBy() {
Collector<String, ?, Optional<String>> c = minBy(Comparator.naturalOrder());
applyItems(Optional.of("a"), c, "a", "z");
applyItems(Optional.of("a"), c, "z", "a");
assertZeroItemsCollectedAs(Optional.empty(), c);
assertSingleItemCollectedAs(Optional.of("foo"), c, "foo");
}
public void testPartitioningBy() {
Collector<Boolean, ?, Map<Boolean, List<Boolean>>> c1 = partitioningBy(Boolean::valueOf);
Map<Boolean, List<Boolean>> mapOfLists = new HashMap<>();
mapOfLists.put(true, Collections.singletonList(true));
mapOfLists.put(false, Collections.singletonList(false));
applyItems(mapOfLists, c1, true, false);
mapOfLists.clear();
mapOfLists.put(false, Collections.emptyList());
mapOfLists.put(true, Arrays.asList(true, true));
applyItems(mapOfLists, c1, true, true);
mapOfLists.clear();
mapOfLists.put(false, Arrays.asList(false, false));
mapOfLists.put(true, Collections.emptyList());
applyItems(mapOfLists, c1, false, false);
mapOfLists.clear();
mapOfLists.put(false, Collections.emptyList());
mapOfLists.put(true, Collections.emptyList());
assertZeroItemsCollectedAs(mapOfLists, c1);
mapOfLists.clear();
mapOfLists.put(false, Collections.emptyList());
mapOfLists.put(true, Collections.singletonList(true));
assertSingleItemCollectedAs(mapOfLists, c1, true);
mapOfLists.clear();
mapOfLists.put(false, Collections.singletonList(false));
mapOfLists.put(true, Collections.emptyList());
assertSingleItemCollectedAs(mapOfLists, c1, false);
Collector<Boolean, ?, Map<Boolean, Set<Boolean>>> c2 =
partitioningBy(Boolean::valueOf, toSet());
Map<Boolean, Set<Boolean>> mapOfSets = new HashMap<>();
mapOfSets.put(true, Collections.singleton(true));
mapOfSets.put(false, Collections.singleton(false));
applyItems(mapOfSets, c2, true, false);
mapOfSets.clear();
mapOfSets.put(false, Collections.emptySet());
mapOfSets.put(true, Collections.singleton(true));
applyItems(mapOfSets, c2, true, true);
mapOfSets.clear();
mapOfSets.put(false, Collections.singleton(false));
mapOfSets.put(true, Collections.emptySet());
applyItems(mapOfSets, c2, false, false);
}
public void testSummarizingDouble() {
Collector<Double, ?, DoubleSummaryStatistics> c = summarizingDouble(Double::doubleValue);
DoubleSummaryStatistics stats = new DoubleSummaryStatistics();
stats.accept(5.1);
stats.accept(7);
BiPredicate<DoubleSummaryStatistics, DoubleSummaryStatistics> equals = (s1, s2) ->
s1.getSum() == s2.getSum()
&& s1.getAverage() == s2.getAverage()
&& s1.getCount() == s2.getCount()
&& s1.getMin() == s2.getMin()
&& s1.getMax() == s2.getMax();
applyItems(stats, c, 5.1, 7.0, equals);
applyItems(stats, c, 7.0, 5.1, equals);//probably unnecessary to run these backward
assertZeroItemsCollectedAs(new DoubleSummaryStatistics(), c, equals);
stats = new DoubleSummaryStatistics();
stats.accept(7.3);
assertSingleItemCollectedAs(stats, c, 7.3, equals);
}
public void testSummarizingInt() {
Collector<Integer, ?, IntSummaryStatistics> c = summarizingInt(Integer::intValue);
IntSummaryStatistics stats = new IntSummaryStatistics();
stats.accept(2);
stats.accept(10);
BiPredicate<IntSummaryStatistics, IntSummaryStatistics> equals = (s1, s2) ->
s1.getSum() == s2.getSum()
&& s1.getAverage() == s2.getAverage()
&& s1.getCount() == s2.getCount()
&& s1.getMin() == s2.getMin()
&& s1.getMax() == s2.getMax();
applyItems(stats, c, 2, 10, equals);
applyItems(stats, c, 10, 2, equals);
assertZeroItemsCollectedAs(new IntSummaryStatistics(), c, equals);
stats = new IntSummaryStatistics();
stats.accept(7);
assertSingleItemCollectedAs(stats, c, 7, equals);
}
public void testSummarizingLong() {
Collector<Long, ?, LongSummaryStatistics> c = summarizingLong(Long::longValue);
LongSummaryStatistics stats = new LongSummaryStatistics();
stats.accept(2);
stats.accept(10);
BiPredicate<LongSummaryStatistics, LongSummaryStatistics> equals = (s1, s2) ->
s1.getSum() == s2.getSum()
&& s1.getAverage() == s2.getAverage()
&& s1.getCount() == s2.getCount()
&& s1.getMin() == s2.getMin()
&& s1.getMax() == s2.getMax();
applyItems(stats, c, 2L, 10L, equals);
applyItems(stats, c, 10L, 2L, equals);
assertZeroItemsCollectedAs(new LongSummaryStatistics(), c, equals);
stats = new LongSummaryStatistics();
stats.accept(7L);
assertSingleItemCollectedAs(stats, c, 7L, equals);
}
public void testSummingDouble() {
Collector<Double, ?, Double> c = summingDouble(Double::doubleValue);
applyItems(4.1 + 8.2, c, 4.1, 8.2);
assertZeroItemsCollectedAs(0d, c);
assertSingleItemCollectedAs(7.3, c, 7.3);
}
public void testSummingInt() {
Collector<Integer, ?, Integer> c = summingInt(Integer::intValue);
applyItems(4 + 8, c, 4, 8);
assertZeroItemsCollectedAs(0, c);
assertSingleItemCollectedAs(7, c, 7);
}
public void testSummingLong() {
Collector<Long, ?, Long> c = summingLong(Long::longValue);
applyItems(4L + 8L, c, 4L, 8L);
assertZeroItemsCollectedAs(0L, c);
assertSingleItemCollectedAs(5L, c, 5L);
}
public void testList() {
Collector<String, ?, List<String>> c = toList();
// same items (allow dups)
applyItems(
Arrays.asList("a", "a"),
c,
"a", "a"
);
// ordered
applyItems(
Arrays.asList("a", "b"),
c,
"a", "b"
);
assertZeroItemsCollectedAs(Collections.emptyList(), c);
assertSingleItemCollectedAs(Collections.singletonList("a"), c, "a");
}
public void testMap() {
Collector<String, ?, Map<String, String>> c = toMap(Function.identity(), Function.identity());
// two distinct items
Map<String, String> map = new HashMap<>();
map.put("a", "a");
map.put("b", "b");
applyItems(map, c, "a", "b");
// inline applyItems and test each to confirm failure for duplicates
try {
applyItemsWithoutSplitting(c, "a", "a");
fail("expected IllegalStateException");
} catch (IllegalStateException expected) {
}
try {
applyItemsWithSplitting(c, "a", "a");
fail("expected IllegalStateException");
} catch (IllegalStateException expected) {
}
assertZeroItemsCollectedAs(Collections.emptyMap(), c);
assertSingleItemCollectedAs(Collections.singletonMap("a", "a"), c, "a");
List<String> seen = new ArrayList<>();
c = toMap(Function.identity(), Function.identity(), (s, s2) -> {
seen.add("first: " + s);
seen.add("second: " + s2);
return s + "," + s2;
});
map = new HashMap<>();
map.put("a", "a,a");
applyItems(map, c, "a", "a");
assertEquals(Arrays.asList("first: a", "second: a", "first: a", "second: a"), seen);
}
public void testSet() {
Collector<String, ?, Set<String>> c = toSet();
// same items (no dups)
applyItems(
Collections.singleton("a"),
c,
"a", "a"
);
// different items
applyItems(
new HashSet<>(Arrays.asList("a", "b")),
c,
"a", "b"
);
assertZeroItemsCollectedAs(Collections.emptySet(), c);
assertSingleItemCollectedAs(Collections.singleton("a"), c, "a");
}
/**
* This method attempts to apply a collector to items as a stream might do, so that we can simply
* verify the output. Taken from the Collector class's javadoc.
*/
private static <T, A, R> void applyItems(
R expected, Collector<T, A, R> collector, T t1, T t2, BiPredicate<R, R> equals) {
assertTrue(
"failed without splitting",
equals.test(expected, applyItemsWithoutSplitting(collector, t1, t2)));
assertTrue(
"failed with splitting", equals.test(expected, applyItemsWithSplitting(collector, t1, t2)));
}
/**
* This method attempts to apply a collector to items as a stream might do, so that we
* can simply verify the output. Taken from the Collector class's javadoc.
*/
private static <T, A, R> void applyItems(R expected, Collector<T, A, R> collector, T t1, T t2) {
applyItems(expected, collector, t1, t2, Object::equals);
}
/**
* Helper for applyItems.
*/
private static <T, A, R> R applyItemsWithoutSplitting(Collector<T, A, R> collector, T t1, T t2) {
Supplier<A> supplier = collector.supplier();
BiConsumer<A, T> accumulator = collector.accumulator();
// unused in this impl
BinaryOperator<A> combiner = collector.combiner();
Function<A, R> finisher = collector.finisher();
A a1 = supplier.get();
accumulator.accept(a1, t1);
accumulator.accept(a1, t2);
// result without splitting
R r1 = finisher.apply(a1);
return r1;
}
/**
* Helper for applyItems.
*/
private static <T, A, R> R applyItemsWithSplitting(Collector<T, A, R> collector, T t1, T t2) {
Supplier<A> supplier = collector.supplier();
BiConsumer<A, T> accumulator = collector.accumulator();
// actually used in this impl
BinaryOperator<A> combiner = collector.combiner();
Function<A, R> finisher = collector.finisher();
A a2 = supplier.get();
accumulator.accept(a2, t1);
A a3 = supplier.get();
accumulator.accept(a3, t2);
// result with splitting
R r2 = finisher.apply(combiner.apply(a2, a3));
return r2;
}
private static <T, A, R> void assertZeroItemsCollectedAs(
R expected, Collector<T, A, R> collector) {
assertZeroItemsCollectedAs(expected, collector, Object::equals);
}
private static <T, A, R> void assertZeroItemsCollectedAs(
R expected, Collector<T, A, R> collector, BiPredicate<R, R> equals) {
Supplier<A> supplier = collector.supplier();
// unused in this impl
BiConsumer<A, T> accumulator = collector.accumulator();
// shouldn't really be used, just handy to poke the internals quick
BinaryOperator<A> combiner = collector.combiner();
Function<A, R> finisher = collector.finisher();
R actual = finisher.apply(supplier.get());
assertTrue(equals, expected, actual);
// doesn't actually ever happen, just internal checks
actual = finisher.apply(combiner.apply(supplier.get(), supplier.get()));
assertTrue(equals, expected, actual);
}
private static <T, A, R> void assertSingleItemCollectedAs(
R expected, Collector<T, A, R> collector, T item) {
assertSingleItemCollectedAs(expected, collector, item, Object::equals);
}
private static <T, A, R> void assertSingleItemCollectedAs(
R expected, Collector<T, A, R> collector, T item, BiPredicate<R, R> equals) {
Supplier<A> supplier = collector.supplier();
BiConsumer<A, T> accumulator = collector.accumulator();
// shouldn't really be used, just handy to poke the internals quick
BinaryOperator<A> combiner = collector.combiner();
Function<A, R> finisher = collector.finisher();
A a1 = supplier.get();
accumulator.accept(a1, item);
R actual = finisher.apply(a1);
// normal test
assertTrue(equals, expected, actual);
// these shouldn't really be used, just handy to poke the internals quick
actual = finisher.apply(combiner.apply(a1, supplier.get()));
assertTrue(equals, expected, actual);
actual = finisher.apply(combiner.apply(supplier.get(), a1));
assertTrue(equals, expected, actual);
}
private static <T, U> void assertTrue(BiPredicate<T, U> predicate, T expected, U actual) {
assertTrue("expected= " + expected + ", actual=" + actual, predicate.test(expected, actual));
}
}