| /* |
| * 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.Arrays.asList; |
| |
| import com.google.gwt.emultest.java.util.EmulTestBase; |
| |
| import com.google.gwt.testing.TestUtils; |
| |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collections; |
| import java.util.Comparator; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Optional; |
| import java.util.Spliterator; |
| import java.util.function.Function; |
| import java.util.function.Supplier; |
| import java.util.stream.Collectors; |
| import java.util.stream.DoubleStream; |
| import java.util.stream.IntStream; |
| import java.util.stream.LongStream; |
| import java.util.stream.Stream; |
| |
| /** |
| * Tests {@link Stream}. |
| */ |
| public class StreamTest extends EmulTestBase { |
| |
| public void testEmptyStream() { |
| Stream<Object> empty = Stream.empty(); |
| assertEquals(0, empty.count()); |
| try { |
| empty.count(); |
| fail("second terminal operation should have thrown IllegalStateEx"); |
| } catch (IllegalStateException expected) { |
| // expected |
| } |
| |
| assertEquals(0, Stream.empty().limit(2).collect(Collectors.toList()).size()); |
| assertEquals(0, Stream.empty().count()); |
| assertEquals(0, Stream.empty().limit(2).count()); |
| |
| assertFalse(Stream.empty().findFirst().isPresent()); |
| assertFalse(Stream.empty().findAny().isPresent()); |
| assertFalse(Stream.<String>empty().max(Comparator.naturalOrder()).isPresent()); |
| assertFalse(Stream.<String>empty().min(Comparator.naturalOrder()).isPresent()); |
| assertTrue(Stream.empty().allMatch(item -> true)); |
| assertFalse(Stream.empty().anyMatch(item -> true)); |
| assertTrue(Stream.empty().noneMatch(item -> false)); |
| assertFalse(Stream.empty().iterator().hasNext()); |
| assertFalse(Stream.empty().spliterator().tryAdvance(a -> fail("should not advance"))); |
| Stream.empty().spliterator().forEachRemaining(a -> fail("should not advance")); |
| assertEquals(new Object[0], Stream.empty().toArray()); |
| assertEquals(new Object[0], Stream.empty().toArray(Object[]::new)); |
| } |
| |
| public void testStreamOfOne() { |
| Supplier<Stream<String>> one = () -> Stream.of(""); |
| assertEquals(Collections.singletonList(""), one.get().collect(Collectors.toList())); |
| assertEquals(1L, one.get().count()); |
| assertEquals("", one.get().findFirst().get()); |
| assertEquals("", one.get().findAny().get()); |
| } |
| |
| public void testBuilder() { |
| Supplier<Stream<String>> s = () -> Stream.<String>builder().add("1").add("3").add("2").build(); |
| |
| Optional<String> max = s.get().filter(str -> !str.equals("3")).max(Comparator.naturalOrder()); |
| assertTrue(max.isPresent()); |
| assertEquals("2", max.get()); |
| |
| max = s.get().max(Comparator.reverseOrder()); |
| assertTrue(max.isPresent()); |
| assertEquals("1", max.get()); |
| |
| Stream.Builder<Object> builder = Stream.builder(); |
| Stream<Object> built = builder.build(); |
| assertEquals(0, built.count()); |
| try { |
| builder.build(); |
| fail("build() after build() should fail"); |
| } catch (IllegalStateException expected) { |
| // expected |
| } |
| try { |
| builder.add("asdf"); |
| fail("add() after build() should fail"); |
| } catch (IllegalStateException expected) { |
| // expected |
| } |
| } |
| |
| public void testConcat() { |
| Supplier<Stream<String>> adbc = () -> Stream.concat(Stream.of("a", "d"), Stream.of("b", "c")); |
| |
| assertEquals(new String[] {"a", "d", "b", "c"}, adbc.get().toArray(String[]::new)); |
| assertEquals(new String[] {"a", "b", "c", "d"}, adbc.get().sorted().toArray(String[]::new)); |
| |
| List<String> closed = new ArrayList<>(); |
| Stream<String> first = Stream.of("first").onClose(() -> closed.add("first")); |
| Stream<String> second = Stream.of("second").onClose(() -> closed.add("second")); |
| |
| Stream<String> concat = Stream.concat(first, second); |
| |
| // read everything, make sure we saw it all and didn't close automatically |
| String collectedAll = concat.collect(Collectors.joining()); |
| assertEquals("firstsecond", collectedAll); |
| assertEquals(0, closed.size()); |
| |
| concat.close(); |
| assertEquals(Arrays.asList("first", "second"), closed); |
| } |
| |
| public void testIterate() { |
| assertEquals( |
| new Integer[] {10, 11, 12, 13, 14}, |
| Stream.iterate(0, i -> i + 1).skip(10).limit(5).toArray(Integer[]::new)); |
| } |
| |
| public void testGenerate() { |
| // infinite, but if you limit it is already too short to skip much |
| assertEquals( |
| new Integer[] {}, |
| Stream.generate(makeGenerator()).limit(4).skip(5).toArray(Integer[]::new)); |
| |
| assertEquals( |
| new Integer[] {10, 11, 12, 13, 14}, |
| Stream.generate(makeGenerator()).skip(10).limit(5).toArray(Integer[]::new)); |
| } |
| |
| private Supplier<Integer> makeGenerator() { |
| return new Supplier<Integer>() { |
| int next = 0; |
| |
| @Override |
| public Integer get() { |
| return next++; |
| } |
| }; |
| } |
| |
| public void testSpliterator() { |
| final String[] values = new String[] {"a", "b", "c"}; |
| |
| Spliterator<String> spliterator = Stream.of(values).spliterator(); |
| assertEquals(3, spliterator.estimateSize()); |
| assertEquals(3, spliterator.getExactSizeIfKnown()); |
| |
| List<String> actualValues = new ArrayList<>(); |
| while (spliterator.tryAdvance(actualValues::add)) { |
| // work is all done in the condition |
| } |
| |
| assertEquals(asList(values), actualValues); |
| } |
| |
| public void testIterator() { |
| final String[] values = new String[] {"a", "b", "c"}; |
| |
| List<String> actualValues = new ArrayList<String>(); |
| Iterator<String> iterator = Stream.of(values).iterator(); |
| while (iterator.hasNext()) { |
| actualValues.add(iterator.next()); |
| } |
| assertEquals(asList(values), actualValues); |
| } |
| |
| public void testForEach() { |
| final String[] values = new String[] {"a", "b", "c"}; |
| |
| List<String> actualValues = new ArrayList<>(); |
| Stream.of(values).forEach(actualValues::add); |
| assertEquals(asList(values), actualValues); |
| } |
| |
| // toArray |
| public void testToArray() { |
| assertEquals(new Object[] {"a", "b"}, asList("a", "b").stream().toArray()); |
| assertEquals(new String[] {"a", "b"}, asList("a", "b").stream().toArray(String[]::new)); |
| } |
| |
| // reduce |
| public void testReduce() { |
| String reduced = Stream.of("a", "b", "c").reduce("", String::concat); |
| assertEquals("abc", reduced); |
| |
| reduced = Stream.<String>of().reduce("initial", String::concat); |
| assertEquals("initial", reduced); |
| |
| Optional<String> maybe = Stream.of("a", "b", "c").reduce(String::concat); |
| assertTrue(maybe.isPresent()); |
| assertEquals("abc", maybe.get()); |
| maybe = Stream.<String>of().reduce(String::concat); |
| assertFalse(maybe.isPresent()); |
| |
| reduced = Stream.of("a", "b", "c").reduce("", String::concat, String::concat); |
| assertEquals("abc", reduced); |
| } |
| |
| public void testCollect() { |
| final String[] values = new String[] {"a", "b", "c"}; |
| |
| String collectedString = |
| Stream.of(values) |
| .collect(StringBuilder::new, StringBuilder::append, StringBuilder::append) |
| .toString(); |
| assertEquals("abc", collectedString); |
| |
| List<String> collectedList = Stream.of(values).collect(Collectors.toList()); |
| assertEquals(asList(values), collectedList); |
| } |
| |
| public void testFilter() { |
| // unconsumed stream never runs filter |
| boolean[] data = {false}; |
| Stream.of(1, 2, 3).filter(i -> data[0] |= true); |
| assertFalse(data[0]); |
| |
| // Nothing's *defined* to care about the Spliterator characteristics, but the implementation |
| // can't actually know the size before executing, so we check the characteristics explicitly. |
| assertFalse( |
| Stream.of("a", "b", "c", "d", "c") |
| .filter(a -> a.equals("a")) |
| .spliterator() |
| .hasCharacteristics(Spliterator.SIZED | Spliterator.SUBSIZED)); |
| |
| // one result |
| assertEquals( |
| Collections.singletonList("a"), |
| Stream.of("a", "b", "c", "d", "c").filter(a -> a.equals("a")).collect(Collectors.toList())); |
| // zero results |
| assertEquals( |
| Collections.emptyList(), |
| Stream.of("a", "b", "c", "d", "c").filter(a -> false).collect(Collectors.toList())); |
| // two results |
| assertEquals( |
| asList("c3", "c5"), |
| Stream.of("a1", "b2", "c3", "d4", "c5") |
| .filter(a -> a.startsWith("c")) |
| .collect(Collectors.toList())); |
| // all |
| assertEquals( |
| asList("a", "b", "c", "d", "c"), |
| Stream.of("a", "b", "c", "d", "c").filter(a -> true).collect(Collectors.toList())); |
| } |
| |
| public void testMap() { |
| // unconsumed stream never runs map |
| boolean[] data = {false}; |
| Stream.of(1, 2, 3).map(i -> data[0] |= true); |
| assertFalse(data[0]); |
| |
| assertEquals( |
| asList("#1", "#2", "#3"), |
| Stream.of(1, 2, 3).map(i -> "#" + i).collect(Collectors.toList())); |
| } |
| |
| public void testPeek() { |
| // unconsumed stream never peeks |
| boolean[] data = {false}; |
| Stream.of(1, 2, 3).peek(i -> data[0] |= true); |
| assertFalse(data[0]); |
| |
| // make sure we saw it all in order |
| List<String> items = asList("a", "b", "c"); |
| List<String> peeked = new ArrayList<>(); |
| items |
| .stream() |
| .peek(peeked::add) |
| .forEach( |
| item -> { |
| // deliberately do nothing, just run |
| }); |
| assertEquals(items, peeked); |
| } |
| |
| // same impl, no parallel in browser |
| public void testFindFirstOrAny() { |
| Optional<String> any = Stream.of("a", "b").findAny(); |
| assertTrue(any.isPresent()); |
| assertEquals("a", any.get()); |
| } |
| |
| public void testAnyMatch() { |
| // all |
| assertTrue(Stream.of("a", "b").anyMatch(s -> true)); |
| // some |
| assertTrue(Stream.of("a", "b").anyMatch(s -> s.equals("a"))); |
| // none |
| assertFalse(Stream.of("a", "b").anyMatch(s -> false)); |
| |
| // With null values. |
| // all |
| assertTrue(Stream.of(null, "a", "b").anyMatch(s -> true)); |
| // some |
| assertTrue(Stream.of(null, "a", "b").anyMatch(s -> s == null)); |
| // none |
| assertFalse(Stream.of(null, "a", "b").anyMatch(s -> false)); |
| } |
| |
| public void testAllMatch() { |
| // all |
| assertTrue(Stream.of("a", "b").allMatch(s -> true)); |
| // some |
| assertFalse(Stream.of("a", "b").allMatch(s -> s.equals("a"))); |
| // none |
| assertFalse(Stream.of("a", "b").allMatch(s -> false)); |
| |
| // With null values. |
| // all |
| assertTrue(Stream.of(null, "a", "b").allMatch(s -> true)); |
| // some |
| assertFalse(Stream.of(null, "a", "b").allMatch(s -> s != null)); |
| // none |
| assertFalse(Stream.of(null, "a", "b").allMatch(s -> false)); |
| } |
| |
| public void testNoneMatch() { |
| // all |
| assertFalse(Stream.of("a", "b").noneMatch(s -> true)); |
| // some |
| assertFalse(Stream.of("a", "b").noneMatch(s -> s.equals("a"))); |
| // none |
| assertTrue(Stream.of("a", "b").noneMatch(s -> false)); |
| |
| // With null values. |
| // all |
| assertFalse(Stream.of(null, "a", "b").noneMatch(s -> true)); |
| // some |
| assertFalse(Stream.of(null, "a", "b").noneMatch(s -> s == null)); |
| // none |
| assertTrue(Stream.of(null, "a", "b").noneMatch(s -> false)); |
| } |
| |
| public void testFlatMap() { |
| assertEquals(0, Stream.<Stream<String>>empty().flatMap(Function.identity()).count()); |
| assertEquals(0, Stream.of(Stream.<String>empty()).flatMap(Function.identity()).count()); |
| assertEquals(0, Stream.of(Stream.of()).flatMap(Function.identity()).count()); |
| assertEquals(1, Stream.of(Stream.of("")).flatMap(Function.identity()).count()); |
| |
| Stream<Stream<String>> strings = Stream.of(Stream.of("a", "b"), Stream.empty(), Stream.of("c")); |
| |
| assertEquals( |
| asList("a", "b", "c"), strings.flatMap(Function.identity()).collect(Collectors.toList())); |
| } |
| |
| public void testMapToPrimitives() { |
| Supplier<Stream<String>> s = () -> Stream.of("1", "2", "10"); |
| |
| assertEquals(new int[] {1, 2, 10}, s.get().mapToInt(Integer::parseInt).toArray()); |
| |
| assertEquals(new long[] {1, 2, 10}, s.get().mapToLong(Long::parseLong).toArray()); |
| |
| assertEquals(new double[] {1, 2, 10}, s.get().mapToDouble(Double::parseDouble).toArray()); |
| } |
| |
| public void testFlatMapToPrimitives() { |
| assertEquals(0, Stream.<IntStream>empty().flatMapToInt(Function.identity()).count()); |
| assertEquals(0, Stream.of(IntStream.empty()).flatMapToInt(Function.identity()).count()); |
| assertEquals(0, Stream.of(IntStream.of()).flatMapToInt(Function.identity()).count()); |
| assertEquals(1, Stream.of(IntStream.of(0)).flatMapToInt(Function.identity()).count()); |
| |
| Stream<IntStream> intStreams = |
| Stream.of(IntStream.of(1, 2), IntStream.empty(), IntStream.of(5)); |
| assertEquals(new int[] {1, 2, 5}, intStreams.flatMapToInt(Function.identity()).toArray()); |
| |
| Stream<LongStream> longStreams = |
| Stream.of(LongStream.of(1, 2), LongStream.empty(), LongStream.of(5)); |
| assertEquals(new long[] {1, 2, 5}, longStreams.flatMapToLong(Function.identity()).toArray()); |
| |
| Stream<DoubleStream> doubleStreams = |
| Stream.of(DoubleStream.of(1, 2), DoubleStream.empty(), DoubleStream.of(5)); |
| assertEquals( |
| new double[] {1, 2, 5}, doubleStreams.flatMapToDouble(Function.identity()).toArray()); |
| } |
| |
| public void testDistinct() { |
| List<String> distinct = |
| asList("a", "b", "c", "b").stream().distinct().collect(Collectors.toList()); |
| assertEquals(3, distinct.size()); |
| assertTrue(distinct.contains("a")); |
| assertTrue(distinct.contains("b")); |
| assertTrue(distinct.contains("c")); |
| } |
| |
| public void testSorted() { |
| List<String> sorted = asList("c", "a", "b").stream().sorted().collect(Collectors.toList()); |
| List<String> reversed = |
| asList("c", "a", "b") |
| .stream() |
| .sorted(Comparator.reverseOrder()) |
| .collect(Collectors.toList()); |
| |
| assertEquals(asList("a", "b", "c"), sorted); |
| assertEquals(asList("c", "b", "a"), reversed); |
| } |
| |
| public void testMinMax() { |
| Supplier<Stream<String>> stream = () -> Stream.of("b", "c", "d", "a"); |
| |
| assertEquals("a", stream.get().min(Comparator.naturalOrder()).orElse(null)); |
| assertEquals("d", stream.get().min(Comparator.reverseOrder()).orElse(null)); |
| assertEquals("a", stream.get().max(Comparator.reverseOrder()).orElse(null)); |
| assertEquals("d", stream.get().max(Comparator.naturalOrder()).orElse(null)); |
| |
| assertFalse(stream.get().filter(a -> false).max(Comparator.naturalOrder()).isPresent()); |
| assertFalse(stream.get().filter(a -> false).min(Comparator.naturalOrder()).isPresent()); |
| } |
| |
| public void testCountLimitSkip() { |
| Supplier<Stream<String>> stream = () -> asList("a", "b", "c", "d").stream(); |
| |
| assertEquals(4, stream.get().count()); |
| |
| assertEquals(4, stream.get().limit(4).count()); |
| assertEquals(4, stream.get().limit(5).count()); |
| assertEquals(3, stream.get().limit(3).count()); |
| |
| assertEquals(3, stream.get().skip(1).limit(3).count()); |
| |
| assertEquals(2, stream.get().limit(3).skip(1).count()); |
| |
| assertEquals(1, stream.get().skip(3).count()); |
| |
| assertEquals(asList("c", "d"), stream.get().skip(2).limit(3).collect(Collectors.toList())); |
| assertEquals( |
| Collections.singletonList("c"), stream.get().skip(2).limit(1).collect(Collectors.toList())); |
| |
| assertEquals(Collections.singletonList("d"), stream.get().skip(3).collect(Collectors.toList())); |
| assertEquals(Collections.emptyList(), stream.get().skip(5).collect(Collectors.toList())); |
| |
| assertEquals(asList("a", "b"), stream.get().limit(2).collect(Collectors.toList())); |
| |
| assertEquals( |
| Collections.singletonList("b"), stream.get().limit(2).skip(1).collect(Collectors.toList())); |
| } |
| |
| // This frustrating test was written first on the JVM stream to discover the basic behavior before |
| // trying to implement it in GWT. As far as I can tell, none of this is clearly described in |
| // javadoc. Also note that it is *not* required to use the returned stream from calling onClose |
| public void testCloseQuirks() { |
| // all subclasses use the same close()/onClose(...) impl, just test once with Stream.empty() |
| |
| Stream<Object> s = Stream.of(1); |
| s.close(); |
| // allow multiple close |
| s.close(); |
| |
| // Add a handler, close, and attempt to re-close - handler only runs the one time |
| int[] calledCount = {0}; |
| s = Stream.empty(); |
| s.onClose(() -> calledCount[0]++); |
| // shouldn't have been called yet |
| assertEquals(0, calledCount[0]); |
| s.close(); |
| // called once |
| assertEquals(1, calledCount[0]); |
| s.close(); |
| // not called again on subsequent closes |
| assertEquals(1, calledCount[0]); |
| |
| if (TestUtils.getJdkVersion() >= 11) { |
| try { |
| s = s.onClose(() -> calledCount[0]++); |
| fail(); |
| } catch (IllegalStateException expected) { |
| } |
| return; |
| } |
| |
| // Add a handler after close, and re-close, the handler will only go off after the _second_ |
| // close |
| calledCount[0] = 0; |
| s = Stream.of(1); |
| s.close(); |
| s = s.onClose(() -> calledCount[0]++); |
| // shouldn't have been called yet |
| assertEquals(0, calledCount[0]); |
| s.close(); |
| // frustratingly, the JVM apparently permits each handler when added to let the stream be closed |
| // _again_ |
| assertEquals(1, calledCount[0]); |
| |
| // Adding yet another runnable and closing again demonstrates this - only the new one is run, |
| // the old ones are not |
| s = s.onClose(() -> calledCount[0]++); |
| s.close(); |
| assertEquals(2, calledCount[0]); |
| |
| // Add two handlers, ensure both are called, and neither called the second time the stream is |
| // closed |
| calledCount[0] = 0; |
| s = Stream.empty(); |
| s = s.onClose(() -> calledCount[0]++); |
| s = s.onClose(() -> calledCount[0]++); |
| s.close(); |
| assertEquals(2, calledCount[0]); |
| s.close(); |
| assertEquals(2, calledCount[0]); |
| } |
| |
| public void testClose() { |
| // terminate stream before closing, confirm that handler is called |
| Stream<Object> s = Stream.of("a", "b", "c"); |
| int[] calledCount = {0}; |
| s = s.onClose(() -> calledCount[0]++); |
| |
| long count = s.count(); |
| assertEquals(3, count); |
| assertEquals(0, calledCount[0]); |
| |
| s.close(); |
| assertEquals(1, calledCount[0]); |
| |
| // terminate stream after closing, confirm that handler is called, and terminating fails |
| s = Stream.of("a", "b", "c"); |
| calledCount[0] = 0; |
| s = s.onClose(() -> calledCount[0]++); |
| |
| s.close(); |
| assertEquals(1, calledCount[0]); |
| |
| try { |
| s.count(); |
| fail("Expected IllegalStateException"); |
| } catch (IllegalStateException expected) { |
| // expected |
| } |
| assertEquals(1, calledCount[0]); |
| } |
| |
| public void testCloseException() { |
| // Try a single exception, confirm we catch it |
| Stream<Object> s = Stream.of(1, 2, 3); |
| |
| RuntimeException a = new RuntimeException("a"); |
| s.onClose( |
| () -> { |
| throw a; |
| }); |
| try { |
| s.close(); |
| fail("RuntimeException expected"); |
| } catch (RuntimeException expected) { |
| assertSame(a, expected); |
| assertEquals(0, expected.getSuppressed().length); |
| } |
| |
| // Throw an exception in two of the three handlers, confirm both arrive and the third was called |
| // correctly |
| s = Stream.of(1, 2, 3); |
| |
| RuntimeException a2 = new RuntimeException("a"); |
| IllegalStateException b = new IllegalStateException("b"); |
| int[] calledCount = {0}; |
| s.onClose( |
| () -> { |
| throw a2; |
| }) |
| .onClose( |
| () -> { |
| throw b; |
| }) |
| .onClose( |
| () -> { |
| throw a2; |
| }) |
| .onClose( |
| () -> { |
| throw b; |
| }) |
| .onClose(() -> calledCount[0]++); |
| |
| try { |
| s.close(); |
| fail("RuntimeException expected"); |
| } catch (RuntimeException expected) { |
| assertSame(a2, expected); |
| assertEquals(new Throwable[] {b, b}, expected.getSuppressed()); |
| } |
| assertEquals(1, calledCount[0]); |
| |
| // Throw the same exception instance twice, ensure it only arrives once |
| s = Stream.of(1, 2, 3); |
| |
| RuntimeException t = new RuntimeException("a"); |
| s.onClose( |
| () -> { |
| throw t; |
| }) |
| .onClose( |
| () -> { |
| throw t; |
| }); |
| |
| try { |
| s.close(); |
| fail("RuntimeException expected"); |
| } catch (RuntimeException expected) { |
| assertSame(t, expected); |
| assertEquals(0, expected.getSuppressed().length); |
| } |
| } |
| } |