/* __ __ __ __ __ ___ * \ \ / / \ \ / / __/ * \ \/ / /\ \ \/ / / * \____/__/ \__\____/__/.ɪᴏ * ᶜᵒᵖʸʳᶦᵍʰᵗ ᵇʸ ᵛᵃᵛʳ ⁻ ˡᶦᶜᵉⁿˢᵉᵈ ᵘⁿᵈᵉʳ ᵗʰᵉ ᵃᵖᵃᶜʰᵉ ˡᶦᶜᵉⁿˢᵉ ᵛᵉʳˢᶦᵒⁿ ᵗʷᵒ ᵈᵒᵗ ᶻᵉʳᵒ */ package io.vavr.collection; import io.vavr.*; import io.vavr.control.Option; import io.vavr.control.Try; import org.junit.Ignore; import org.junit.Test; import java.io.InvalidObjectException; import java.math.BigDecimal; import java.util.ArrayList; import java.util.Spliterator; import java.util.function.Function; import java.util.function.Predicate; import java.util.function.Supplier; import java.util.stream.Collector; import static io.vavr.collection.Stream.concat; public class StreamTest extends AbstractLinearSeqTest { // -- construction @Override protected <T> Collector<T, ArrayList<T>, Stream<T>> collector() { return Stream.collector(); } @Override protected <T> Stream<T> empty() { return Stream.empty(); } @Override protected <T> Stream<T> of(T element) { return Stream.of(element); } @SuppressWarnings("varargs") @SafeVarargs @Override protected final <T> Stream<T> of(T... elements) { return Stream.of(elements); } @Override protected <T> Stream<T> ofAll(Iterable<? extends T> elements) { return Stream.ofAll(elements); } @Override protected <T extends Comparable<? super T>> Stream<T> ofJavaStream(java.util.stream.Stream<? extends T> javaStream) { return Stream.ofAll(javaStream); } @Override protected Stream<Boolean> ofAll(boolean... elements) { return Stream.ofAll(elements); } @Override protected Stream<Byte> ofAll(byte... elements) { return Stream.ofAll(elements); } @Override protected Stream<Character> ofAll(char... elements) { return Stream.ofAll(elements); } @Override protected Stream<Double> ofAll(double... elements) { return Stream.ofAll(elements); } @Override protected Stream<Float> ofAll(float... elements) { return Stream.ofAll(elements); } @Override protected Stream<Integer> ofAll(int... elements) { return Stream.ofAll(elements); } @Override protected Stream<Long> ofAll(long... elements) { return Stream.ofAll(elements); } @Override protected Stream<Short> ofAll(short... elements) { return Stream.ofAll(elements); } @Override protected <T> Stream<T> tabulate(int n, Function<? super Integer, ? extends T> f) { return Stream.tabulate(n, f); } @Override protected <T> Stream<T> fill(int n, Supplier<? extends T> s) { return Stream.fill(n, s); } @Override protected Stream<Character> range(char from, char toExclusive) { return Stream.range(from, toExclusive); } @Override protected Stream<Character> rangeBy(char from, char toExclusive, int step) { return Stream.rangeBy(from, toExclusive, step); } @Override protected Stream<Double> rangeBy(double from, double toExclusive, double step) { return Stream.rangeBy(from, toExclusive, step); } @Override protected Stream<Integer> range(int from, int toExclusive) { return Stream.range(from, toExclusive); } @Override protected Stream<Integer> rangeBy(int from, int toExclusive, int step) { return Stream.rangeBy(from, toExclusive, step); } @Override protected Stream<Long> range(long from, long toExclusive) { return Stream.range(from, toExclusive); } @Override protected Stream<Long> rangeBy(long from, long toExclusive, long step) { return Stream.rangeBy(from, toExclusive, step); } @Override protected Stream<Character> rangeClosed(char from, char toInclusive) { return Stream.rangeClosed(from, toInclusive); } @Override protected Stream<Character> rangeClosedBy(char from, char toInclusive, int step) { return Stream.rangeClosedBy(from, toInclusive, step); } @Override protected Stream<Double> rangeClosedBy(double from, double toInclusive, double step) { return Stream.rangeClosedBy(from, toInclusive, step); } @Override protected Stream<Integer> rangeClosed(int from, int toInclusive) { return Stream.rangeClosed(from, toInclusive); } @Override protected Stream<Integer> rangeClosedBy(int from, int toInclusive, int step) { return Stream.rangeClosedBy(from, toInclusive, step); } @Override protected Stream<Long> rangeClosed(long from, long toInclusive) { return Stream.rangeClosed(from, toInclusive); } @Override protected Stream<Long> rangeClosedBy(long from, long toInclusive, long step) { return Stream.rangeClosedBy(from, toInclusive, step); } @Override @SuppressWarnings("unchecked") protected <T> Stream<Stream<T>> transpose(Seq<? extends Seq<T>> rows) { return Stream.transpose((Stream<Stream<T>>) rows); } @Override protected boolean useIsEqualToInsteadOfIsSameAs() { return true; } // -- static concat() @Test public void shouldConcatEmptyIterableIterable() { final Iterable<Iterable<Integer>> empty = List.empty(); assertThat(concat(empty)).isSameAs(empty()); } @Test public void shouldConcatNonEmptyIterableIterable() { final Iterable<Iterable<Integer>> itIt = List.of(List.of(1, 2), List.of(3)); assertThat(concat(itIt)).isEqualTo(of(1, 2, 3)); } @Test public void shouldConcatEmptyArrayIterable() { assertThat(concat()).isSameAs(empty()); } @Test public void shouldConcatNonEmptyArrayIterable() { assertThat(concat(List.of(1, 2), List.of(3))).isEqualTo(of(1, 2, 3)); } // -- static from(int) @Test public void shouldGenerateIntStream() { assertThat(Stream.from(-1).take(3)).isEqualTo(Stream.of(-1, 0, 1)); } @Test public void shouldGenerateOverflowingIntStream() { //noinspection NumericOverflow assertThat(Stream.from(Integer.MAX_VALUE).take(2)) .isEqualTo(Stream.of(Integer.MAX_VALUE, Integer.MAX_VALUE + 1)); } // -- static from(int, int) @Test public void shouldGenerateIntStreamWithStep() { assertThat(Stream.from(-1, 6).take(3)).isEqualTo(Stream.of(-1, 5, 11)); } @Test public void shouldGenerateOverflowingIntStreamWithStep() { //noinspection NumericOverflow assertThat(Stream.from(Integer.MAX_VALUE, 2).take(2)) .isEqualTo(Stream.of(Integer.MAX_VALUE, Integer.MAX_VALUE + 2)); } // -- static from(long) @Test public void shouldGenerateLongStream() { assertThat(Stream.from(-1L).take(3)).isEqualTo(Stream.of(-1L, 0L, 1L)); } @Test public void shouldGenerateOverflowingLongStream() { //noinspection NumericOverflow assertThat(Stream.from(Long.MAX_VALUE).take(2)) .isEqualTo(Stream.of(Long.MAX_VALUE, Long.MAX_VALUE + 1)); } // -- static from(long, long) @Test public void shouldGenerateLongStreamWithStep() { assertThat(Stream.from(-1L, 5L).take(3)).isEqualTo(Stream.of(-1L, 4L, 9L)); } @Test public void shouldGenerateOverflowingLongStreamWithStep() { //noinspection NumericOverflow assertThat(Stream.from(Long.MAX_VALUE, 2).take(2)) .isEqualTo(Stream.of(Long.MAX_VALUE, Long.MAX_VALUE + 2)); } // -- static continually(Supplier) @Test public void shouldGenerateInfiniteStreamBasedOnSupplier() { assertThat(Stream.continually(() -> 1).take(13).reduce((i, j) -> i + j)).isEqualTo(13); } // -- static iterate(T, Function) @Test public void shouldGenerateInfiniteStreamBasedOnSupplierWithAccessToPreviousValue() { assertThat(Stream.iterate(2, (i) -> i + 2).take(3).reduce((i, j) -> i + j)).isEqualTo(12); } // -- static continually (T) @Test public void shouldGenerateInfiniteStreamBasedOnRepeatedElement() { assertThat(Stream.continually(2).take(3).reduce((i, j) -> i + j)).isEqualTo(6); } // -- static cons(T, Supplier) @Test public void shouldBuildStreamBasedOnHeadAndTailSupplierWithAccessToHead() { assertThat(Stream.cons(1, () -> Stream.cons(2, Stream::empty))).isEqualTo(Stream.of(1, 2)); } // -- static narrow @Test public void shouldNarrowStream() { final Stream<Double> doubles = of(1.0d); final Stream<Number> numbers = Stream.narrow(doubles); final int actual = numbers.append(new BigDecimal("2.0")).sum().intValue(); assertThat(actual).isEqualTo(3); } // -- append @Test public void shouldAppendMillionTimes() { final int bigNum = 1_000_000; assertThat(Stream.range(0, bigNum).foldLeft(Stream.empty(), Stream::append).length()).isEqualTo(bigNum); } // -- appendAll @Test public void shouldAppendAll() { assertThat(of(1, 2, 3).appendAll(of(4, 5, 6))).isEqualTo(of(1, 2, 3, 4, 5, 6)); } @Test public void shouldAppendAllIfThisIsEmpty() { assertThat(empty().appendAll(of(4, 5, 6))).isEqualTo(of(4, 5, 6)); } @Test public void shouldAppendAllIfThatIsInfinite() { assertThat(of(1, 2, 3).appendAll(Stream.from(4)).take(6)).isEqualTo(of(1, 2, 3, 4, 5, 6)); } @Test public void shouldAppendAllToInfiniteStream() { assertThat(Stream.from(1).appendAll(Stream.continually(() -> -1)).take(6)).isEqualTo(of(1, 2, 3, 4, 5, 6)); } // -- combinations @Test public void shouldComputeCombinationsOfEmptyStream() { assertThat(Stream.empty().combinations()).isEqualTo(Stream.of(Stream.empty())); } @Test public void shouldComputeCombinationsOfNonEmptyStream() { assertThat(Stream.of(1, 2, 3).combinations()).isEqualTo(Stream.of(Stream.empty(), Stream.of(1), Stream.of(2), Stream.of(3), Stream.of(1, 2), Stream.of(1, 3), Stream.of(2, 3), Stream.of(1, 2, 3))); } // -- combinations(k) @Test public void shouldComputeKCombinationsOfEmptyStream() { assertThat(Stream.empty().combinations(1)).isEqualTo(Stream.empty()); } @Test public void shouldComputeKCombinationsOfNonEmptyStream() { assertThat(Stream.of(1, 2, 3).combinations(2)) .isEqualTo(Stream.of(Stream.of(1, 2), Stream.of(1, 3), Stream.of(2, 3))); } // -- flatMap @Test public void shouldFlatMapInfiniteTraversable() { assertThat(Stream.iterate(1, i -> i + 1).flatMap(i -> List.of(i, 2 * i)).take(7)) .isEqualTo(Stream.of(1, 2, 2, 4, 3, 6, 4)); } // -- peek @Override protected int getPeekNonNilPerformingAnAction() { return 3; } // -- permutations @Test public void shouldComputePermutationsOfEmptyStream() { assertThat(Stream.empty().permutations()).isEqualTo(Stream.empty()); } @Test public void shouldComputePermutationsOfNonEmptyStream() { assertThat(Stream.of(1, 2, 3).permutations()).isEqualTo(Stream.ofAll(Stream.of(Stream.of(1, 2, 3), Stream.of(1, 3, 2), Stream.of(2, 1, 3), Stream.of(2, 3, 1), Stream.of(3, 1, 2), Stream.of(3, 2, 1)))); } // -- appendSelf @Test public void shouldRecurrentlyCalculateFibonacci() { assertThat(Stream.of(1, 1).appendSelf(self -> self.zip(self.tail()).map(t -> t._1 + t._2)).take(10)) .isEqualTo(Stream.of(1, 1, 2, 3, 5, 8, 13, 21, 34, 55)); } @Test public void shouldRecurrentlyCalculatePrimes() { assertThat(Stream .of(2) .appendSelf(self -> Stream .iterate(3, i -> i + 2) .filter(i -> self.takeWhile(j -> j * j <= i).forAll(k -> i % k > 0))) .take(10)).isEqualTo(Stream.of(2, 3, 5, 7, 11, 13, 17, 19, 23, 29)); } @Test public void shouldDoNothingOnNil() { assertThat(Stream.empty().appendSelf(self -> self)).isEqualTo(Stream.empty()); } @Test public void shouldRecurrentlyCalculateArithmeticProgression() { assertThat(Stream.of(1).appendSelf(self -> self.map(t -> t + 1)).take(4)).isEqualTo(Stream.of(1, 2, 3, 4)); } @Test public void shouldRecurrentlyCalculateGeometricProgression() { assertThat(Stream.of(1).appendSelf(self -> self.map(t -> t * 2)).take(4)).isEqualTo(Stream.of(1, 2, 4, 8)); } // -- containsSlice @Test public void shouldRecognizeInfiniteDoesContainSlice() { final boolean actual = Stream.iterate(1, i -> i + 1).containsSlice(of(12, 13, 14)); assertThat(actual).isTrue(); } // -- cycle @Test public void shouldCycleEmptyStream() { assertThat(empty().cycle()).isEqualTo(empty()); } @Test public void shouldCycleNonEmptyStream() { assertThat(of(1, 2, 3).cycle().take(9)).isEqualTo(of(1, 2, 3, 1, 2, 3, 1, 2, 3)); } // -- cycle(int) @Test public void shouldCycleTimesEmptyStream() { assertThat(empty().cycle(3)).isEqualTo(empty()); } @Test public void shouldCycleTimesNonEmptyStream() { assertThat(of(1, 2, 3).cycle(-1)).isEqualTo(empty()); assertThat(of(1, 2, 3).cycle(0)).isEqualTo(empty()); assertThat(of(1, 2, 3).cycle(1)).isEqualTo(of(1, 2, 3)); assertThat(of(1, 2, 3).cycle(3)).isEqualTo(of(1, 2, 3, 1, 2, 3, 1, 2, 3)); } // -- dropRight @Test public void shouldLazyDropRight() { assertThat(Stream.from(1).takeUntil(i -> i == 18).dropRight(7)).isEqualTo(Stream.range(1, 11)); } // -- extend @Test public void shouldExtendStreamWithConstantValue() { assertThat(Stream.of(1, 2, 3).extend(42).take(6)).isEqualTo(of(1, 2, 3, 42, 42, 42)); } @Test public void shouldExtendStreamWithSupplier() { assertThat(Stream.of(1, 2, 3).extend(() -> 42).take(6)).isEqualTo(of(1, 2, 3, 42, 42, 42)); } @Test public void shouldExtendStreamWithFunction() { assertThat(Stream.of(1, 2, 3).extend(i -> i + 1).take(6)).isEqualTo(of(1, 2, 3, 4, 5, 6)); } @Test public void shouldExtendEmptyStreamWithConstantValue() { assertThat(Stream.of().extend(42).take(6)).isEqualTo(of(42, 42, 42, 42, 42, 42)); } @Test public void shouldExtendEmptyStreamWithSupplier() { assertThat(Stream.of().extend(() -> 42).take(6)).isEqualTo(of(42, 42, 42, 42, 42, 42)); } @Test public void shouldReturnAnEmptyStreamWhenExtendingAnEmptyStreamWithFunction() { assertThat(Stream.<Integer> of().extend(i -> i + 1)).isEqualTo(of()); } @Test public void shouldReturnTheOriginalStreamWhenTryingToExtendInfiniteStreamWithConstantValue() { assertThat(Stream.continually(1).extend(42).take(6)).isEqualTo(of(1, 1, 1, 1, 1, 1)); } @Test public void shouldReturnTheOriginalStreamWhenTryingToExtendInfiniteStreamWithSupplier() { assertThat(Stream.continually(1).extend(() -> 42).take(6)).isEqualTo(of(1, 1, 1, 1, 1, 1)); } @Test public void shouldReturnTheOriginalStreamWhenTryingToExtendInfiniteStreamWithFunction() { assertThat(Stream.continually(1).extend(i -> i + 1).take(6)).isEqualTo(of(1, 1, 1, 1, 1, 1)); } // -- isLazy @Override @Test public void shouldVerifyLazyProperty() { assertThat(empty().isLazy()).isTrue(); assertThat(of(1).isLazy()).isTrue(); } // -- subSequence(int, int) @Ignore @Override @Test public void shouldReturnSameInstanceIfSubSequenceStartsAtZeroAndEndsAtLastElement() { // Stream is lazy } // -- tail @Test public void shouldEvaluateTailAtMostOnce() { final int[] counter = { 0 }; final Stream<Integer> stream = Stream.continually(() -> counter[0]++); // this test ensures that the `tail.append(100)` does not modify the tail elements final Stream<Integer> tail = stream.tail().append(100); final String expected = stream.drop(1).take(3).mkString(","); final String actual = tail.take(3).mkString(","); assertThat(expected).isEqualTo("1,2,3"); assertThat(actual).isEqualTo(expected); } @Test public void shouldNotProduceStackOverflow() { Stream.range(0, 1_000_000) .map(String::valueOf) .foldLeft(Stream.<String> empty(), Stream::append) .mkString(); } @Test // See #327, #594 public void shouldNotEvaluateHeadOfTailWhenCallingIteratorHasNext() { final Integer[] vals = new Integer[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; final CheckedFunction2<StringBuilder, Integer, Void> doStuff = (builder, i) -> { builder.append(i); if (i == 5) { throw new Exception("Some error !!!"); } else { return null; } }; final StringBuilder actual = new StringBuilder(); final CheckedFunction1<Integer, Void> consumer1 = doStuff.apply(actual); Stream.of(vals) .map(v -> Try.run(() -> consumer1.apply(v))) .find(Try::isFailure) .getOrElse(() -> Try.success(null)); final StringBuilder expected = new StringBuilder(); final CheckedFunction1<Integer, Void> consumer2 = doStuff.apply(expected); java.util.stream.Stream.of(vals) .map(v -> Try.run(() -> consumer2.apply(v))) .filter(Try::isFailure) .findFirst() .orElseGet(() -> Try.success(null)); assertThat(actual.toString()).isEqualTo(expected.toString()); } // -- take @Test public void shouldNotEvaluateNplusOneWhenTakeN() { final Predicate<Integer> hiddenThrow = i -> { if (i == 0) { return true; } else { throw new IllegalArgumentException(); } }; assertThat(Stream.from(0).filter(hiddenThrow).take(1).sum().intValue()).isEqualTo(0); } @Ignore @Override @Test public void shouldReturnSameInstanceIfTakeAll() { // the size of a possibly infinite stream is unknown } // -- toStream @Test public void shouldReturnSelfOnConvertToStream() { final Value<Integer> value = of(1, 2, 3); assertThat(value.toStream()).isSameAs(value); } // -- toString @Test public void shouldStringifyNil() { assertThat(empty().toString()).isEqualTo("Stream()"); } @Test public void shouldStringifyNonNil() { assertThat(of(1, 2, 3).toString()).isEqualTo("Stream(1, ?)"); } @Test public void shouldStringifyNonNilEvaluatingFirstTail() { final Stream<Integer> stream = this.of(1, 2, 3); stream.tail(); // evaluates second head element assertThat(stream.toString()).isEqualTo("Stream(1, 2, ?)"); } @Test public void shouldStringifyNonNilAndNilTail() { final Stream<Integer> stream = this.of(1); stream.tail(); // evaluates empty tail assertThat(stream.toString()).isEqualTo("Stream(1)"); } // -- transform() @Test public void shouldTransform() { String transformed = of(42).transform(v -> String.valueOf(v.get())); assertThat(transformed).isEqualTo("42"); } // -- unfold @Test public void shouldUnfoldRightToEmpty() { assertThat(Stream.unfoldRight(0, x -> Option.none())).isEqualTo(empty()); } @Test public void shouldUnfoldRightSimpleStream() { assertThat( Stream.unfoldRight(10, x -> x == 0 ? Option.none() : Option.of(new Tuple2<>(x, x - 1)))) .isEqualTo(of(10, 9, 8, 7, 6, 5, 4, 3, 2, 1)); } @Test public void shouldUnfoldLeftToEmpty() { assertThat(Stream.unfoldLeft(0, x -> Option.none())).isEqualTo(empty()); } @Test public void shouldUnfoldLeftSimpleStream() { assertThat( Stream.unfoldLeft(10, x -> x == 0 ? Option.none() : Option.of(new Tuple2<>(x - 1, x)))) .isEqualTo(of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)); } @Test public void shouldUnfoldToEmpty() { assertThat(Stream.unfold(0, x -> Option.none())).isEqualTo(empty()); } @Test public void shouldUnfoldSimpleStream() { assertThat( Stream.unfold(10, x -> x == 0 ? Option.none() : Option.of(new Tuple2<>(x - 1, x)))) .isEqualTo(of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)); } // -- Serializable @Test(expected = InvalidObjectException.class) public void shouldNotSerializeEnclosingClassOfCons() throws Throwable { Serializables.callReadObject(Stream.cons(1, Stream::empty)); } @Test(expected = InvalidObjectException.class) public void shouldNotDeserializeStreamWithSizeLessThanOne() throws Throwable { try { /* * This implementation is stable regarding jvm impl changes of object serialization. The index of the number * of Stream elements is gathered dynamically. */ final byte[] listWithOneElement = Serializables.serialize(Stream.of(0)); final byte[] listWithTwoElements = Serializables.serialize(Stream.of(0, 0)); int index = -1; for (int i = 0; i < listWithOneElement.length && index == -1; i++) { final byte b1 = listWithOneElement[i]; final byte b2 = listWithTwoElements[i]; if (b1 != b2) { if (b1 != 1 || b2 != 2) { throw new IllegalStateException("Difference does not indicate number of elements."); } else { index = i; } } } if (index == -1) { throw new IllegalStateException("Hack incomplete - index not found"); } /* * Hack the serialized data and fake zero elements. */ listWithOneElement[index] = 0; Serializables.deserialize(listWithOneElement); } catch (IllegalStateException x) { throw (x.getCause() != null) ? x.getCause() : x; } } // -- spliterator @Test public void shouldNotHaveSizedSpliterator() { assertThat(of(1, 2, 3).spliterator().hasCharacteristics(Spliterator.SIZED | Spliterator.SUBSIZED)).isFalse(); } @Test public void shouldReturnSizeWhenSpliterator() { assertThat(of(1, 2, 3).spliterator().getExactSizeIfKnown()).isEqualTo(-1); } }