All Downloads are FREE. Search and download functionalities are using the official Maven repository.

co.unruly.matchers.StreamMatchers Maven / Gradle / Ivy

package co.unruly.matchers;

import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.hamcrest.TypeSafeMatcher;

import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.PrimitiveIterator;
import java.util.Objects;
import java.util.stream.*;

public class StreamMatchers {

    public static > Matcher> empty() {
        return new TypeSafeMatcher>() {

            private Iterator actualIterator;

            @Override
            protected boolean matchesSafely(BaseStream actual) {
                actualIterator = actual.iterator();
                return !actualIterator.hasNext();
            }

            @Override
            public void describeTo(Description description) {
                description.appendText("An empty Stream");
            }

            @Override
            protected void describeMismatchSafely(BaseStream item, Description description) {
                description.appendText("A non empty Stream starting with ").appendValue(actualIterator.next());
            }
        };
    }

    /**
     * A matcher for a finite Stream producing the same number of items as the expected Stream,
     * and producing equal items as expected in the same order.
     *
     * For infinite Streams use {@link #startsWith}
     *
     * @param expected A BaseStream against which to compare
     * @param  The type of items produced by each BaseStream
     * @param  The type of BaseStream
     * @see #startsWith
     * @see #startsWithInt
     * @see #startsWithLong
     * @see #startsWithDouble
     */
    public static > Matcher> equalTo(BaseStream expected) {
        return new BaseStreamMatcher>() {
            @Override
            protected boolean matchesSafely(BaseStream actual) {
                return remainingItemsEqual(expected.iterator(), actual.iterator());
            }
        };
    }

    /**
     * A matcher for potentially infinite Streams of objects where the first limit items from each must be
     * equal
     *
     * @param expected A Stream to check against
     * @param limit Only check this number of items from actual Stream
     * @param  The type of items produced by each Stream
     * @see #equalTo
     * @see #startsWithInt
     * @see #startsWithLong
     * @see #startsWithDouble
     */
    public static  Matcher> startsWith(Stream expected, long limit) {
        return new BaseStreamMatcher>() {
            @Override
            protected boolean matchesSafely(Stream actual) {
                return remainingItemsEqual(expected.limit(limit).iterator(), actual.limit(limit).iterator());
            }
        };
    }

    /**
     * A matcher for potentially infinite Streams of primitive doubles where the first limit items from each must be
     * equal
     *
     * @param expected A Stream to check against
     * @param limit Only check this number of items from actual Stream
     * @see #equalTo
     * @see #startsWith
     * @see #startsWithInt
     * @see #startsWithLong
     */
    public static Matcher startsWith(DoubleStream expected, long limit) {
        return new BaseStreamMatcher() {
            @Override
            protected boolean matchesSafely(DoubleStream actual) {
                return remainingItemsEqual(expected.limit(limit).iterator(), actual.limit(limit).iterator());
            }
        };
    }

    /**
     * A matcher for potentially infinite Streams of primitive ints where the first limit items from each must be
     * equal
     *
     * @param expected A Stream to check against
     * @param limit Only check this number of items from actual Stream
     * @see #equalTo
     * @see #startsWith
     * @see #startsWithLong
     * @see #startsWithDouble
     */
    public static Matcher startsWith(IntStream expected, long limit) {
        return new BaseStreamMatcher() {
            @Override
            protected boolean matchesSafely(IntStream actual) {
                return remainingItemsEqual(expected.limit(limit).iterator(), actual.limit(limit).iterator());
            }
        };
    }

    /**
     * A matcher for potentially infinite Streams of primitive ints where the first limit items from each must be
     * equal
     *
     * @param expected A Stream to check against
     * @param limit Only check this number of items from actual Stream
     * @see #equalTo
     * @see #startsWith
     * @see #startsWithInt
     * @see #startsWithDouble
     */
    public static Matcher startsWith(LongStream expected, long limit) {
        return new BaseStreamMatcher() {
            @Override
            protected boolean matchesSafely(LongStream actual) {
                return remainingItemsEqual(expected.limit(limit).iterator(), actual.limit(limit).iterator());
            }
        };
    }

    private static void describeToStartsAllWith(Description description, long limit, Matcher matcher) {
        description
                .appendText("First ")
                .appendText(Long.toString(limit))
                .appendText(" to match ")
                .appendValue(matcher);
    }

    /**
     * A matcher for potentially infinite Streams of objects, the first limit of which must match the given Matcher
     *
     * @param matcher A matcher to apply to items produced from the Stream
     * @param limit Only check this number of items from the Stream
     * @see #allMatch
     * @see #startsWithAllLong
     * @see #startsWithAllInt
     * @see #startsWithAllDouble
     */
    public static  Matcher> startsWithAll(Matcher matcher, long limit) {
        return new StreamAllMatches(matcher) {
            @Override
            protected boolean matchesSafely(Stream actual) {
                return super.matchesSafely(actual.limit(limit));
            }

            @Override
            public void describeTo(Description description) {
                describeToStartsAllWith(description, limit, matcher);
            }
        };

    }

    /**
     * A matcher for potentially infinite Streams of primitive longs, the first limit of which must match the given Matcher
     *
     * @param matcher A matcher to apply to items produced from the Stream
     * @param limit Only check this number of items from the Stream
     * @see #allMatchLong
     * @see #startsWithAll
     * @see #startsWithAllInt
     * @see #startsWithAllDouble
     */
    public static Matcher startsWithAllLong(Matcher matcher, long limit) {
        return new LongStreamAllMatches(matcher) {
            @Override
            protected boolean matchesSafely(LongStream actual) {
                return super.matchesSafely(actual.limit(limit));
            }

            @Override
            public void describeTo(Description description) {
                describeToStartsAllWith(description, limit, matcher);
            }
        };
    }

    /**
     * A matcher for potentially infinite Streams of primitive ints, the first limit of which must match the given Matcher
     *
     * @param matcher A matcher to apply to items produced from the Stream
     * @param limit Only check this number of items from the Stream
     * @see #allMatchInt
     * @see #startsWithAll
     * @see #startsWithAllLong
     * @see #startsWithAllDouble
     */
    public static Matcher startsWithAllInt(Matcher matcher, long limit) {
        return new IntStreamAllMatches(matcher) {
            @Override
            protected boolean matchesSafely(IntStream actual) {
                return super.matchesSafely(actual.limit(limit));
            }

            @Override
            public void describeTo(Description description) {
                describeToStartsAllWith(description, limit, matcher);
            }
        };
    }

    /**
     * A matcher for potentially infinite Streams of primitive doubles, the first limit of which must match the given Matcher
     *
     * @param matcher A matcher to apply to items produced from the Stream
     * @param limit Only check this number of items from the Stream
     * @see #allMatchDouble
     * @see #startsWithAll
     * @see #startsWithAllInt
     * @see #startsWithAllLong
     */
    public static Matcher startsWithAllDouble(Matcher matcher, long limit) {
        return new DoubleStreamAllMatches(matcher) {
            @Override
            protected boolean matchesSafely(DoubleStream actual) {
                return super.matchesSafely(actual.limit(limit));
            }

            @Override
            public void describeTo(Description description) {
                describeToStartsAllWith(description, limit, matcher);
            }
        };
    }

    private static void describeToStartsAnyWith(Description description, long limit, Matcher matcher) {
        description
                .appendText("Any of first ")
                .appendText(Long.toString(limit))
                .appendText(" to match ")
                .appendValue(matcher);
    }

    /**
     * A matcher for potentially infinite Streams of objects,
     * at least one of the first limit of which must match the given Matcher
     *
     * @param matcher A matcher to apply to items produced from the Stream
     * @param limit Only check this number of items from the Stream
     * @see #anyMatch
     * @see #startsWithAnyInt
     * @see #startsWithAnyLong
     * @see #startsWithAnyDouble
     */
    public static  Matcher> startsWithAny(Matcher matcher, long limit) {
        return new StreamAnyMatches(matcher) {
            @Override
            protected boolean matchesSafely(Stream actual) {
                return super.matchesSafely(actual.limit(limit));
            }

            @Override
            public void describeTo(Description description) {
                describeToStartsAnyWith(description, limit, matcher);
            }
        };
    }

    /**
     * A matcher for potentially infinite Streams of primitive longs,
     * at least one of the first limit of which must match the given Matcher
     *
     * @param matcher A matcher to apply to items produced from the Stream
     * @param limit Only check this number of items from the Stream
     * @see #anyMatchLong
     * @see #startsWithAny
     * @see #startsWithAnyInt
     * @see #startsWithAnyDouble
     */
    public static Matcher startsWithAnyLong(Matcher matcher, long limit) {
        return new LongStreamAnyMatches(matcher) {
            @Override
            protected boolean matchesSafely(LongStream actual) {
                return super.matchesSafely(actual.limit(limit));
            }

            @Override
            public void describeTo(Description description) {
                describeToStartsAnyWith(description, limit, matcher);
            }
        };
    }

    /**
     * A matcher for potentially infinite Streams of primitive doubles,
     * at least one of the first limit of which must match the given Matcher
     *
     * @param matcher A matcher to apply to items produced from the Stream
     * @param limit Only check this number of items from the Stream
     * @see #anyMatchDouble
     * @see #startsWithAny
     * @see #startsWithAnyInt
     * @see #startsWithAnyLong
     */
    public static Matcher startsWithAnyDouble(Matcher matcher, long limit) {
        return new DoubleStreamAnyMatches(matcher) {
            @Override
            protected boolean matchesSafely(DoubleStream actual) {
                return super.matchesSafely(actual.limit(limit));
            }

            @Override
            public void describeTo(Description description) {
                describeToStartsAnyWith(description, limit, matcher);
            }
        };
    }

    /**
     * A matcher for potentially infinite Streams of primitive ints,
     * at least one of the first limit of which must match the given Matcher
     *
     * @param matcher A matcher to apply to items produced from the Stream
     * @param limit Only check this number of items from the Stream
     * @see #anyMatchInt
     * @see #startsWithAny
     * @see #startsWithAnyLong
     * @see #startsWithAnyDouble
     */
    public static Matcher startsWithAnyInt(Matcher matcher, long limit) {
        return new IntStreamAnyMatches(matcher) {
            @Override
            protected boolean matchesSafely(IntStream actual) {
                return super.matchesSafely(actual.limit(limit));
            }

            @Override
            public void describeTo(Description description) {
                describeToStartsAnyWith(description, limit, matcher);
            }
        };
    }

    /**
     * The BaseStream must produce exactly the given expected items in order, and no more.
     *
     * For infinite BaseStreams see {@link #startsWith(T...)} or a primitive stream variant
     * @param expected The items that should be produced by the BaseStream
     * @param  The type of items
     * @param  The type of the BaseStream
     * @see #startsWith(T...)
     * @see #startsWithInt(int...)
     * @see #startsWithLong(long...)
     * @see #startsWithDouble(double...)
     */
    @SafeVarargs
    public static > Matcher> contains(T... expected) {
        return new BaseStreamMatcher>() {
            @Override
            protected boolean matchesSafely(BaseStream actual) {
                return remainingItemsEqual(new ArrayIterator<>(expected), actual.iterator());
            }
        };
    }

    /**
     * A matcher for a finite Stream of objects, all of which must match the given Matcher.
     *
     * For infinite Streams see {@link #startsWithAll}
     *
     * @param matcher A Matcher against which to compare items from the Stream
     * @param  The type of items produced by the Stream
     * @see #startsWithAll
     * @see #allMatchInt
     * @see #allMatchLong
     * @see #allMatchDouble
     */
    public static  Matcher> allMatch(Matcher matcher) {
        return new StreamAllMatches(matcher) {
            @Override
            public void describeTo(Description description) {
                description.appendText("All to match ").appendValue(matcher);
            }
        };
    }

    /**
     * A matcher for a finite Stream of primitive ints, all of which must match the given Matcher.
     *
     * For infinite Streams see {@link #startsWithAllInt}
     *
     * @param matcher A Matcher against which to compare items from the Stream
     * @see #startsWithAllInt
     * @see #allMatch
     * @see #allMatchLong
     * @see #allMatchDouble
     */
    public static Matcher allMatchInt(Matcher matcher) {
        return new IntStreamAllMatches(matcher) {
            @Override
            public void describeTo(Description description) {
                description.appendText("All to match ").appendValue(matcher);
            }
        };
    }

    /**
     * A matcher for a finite Stream of primitive longs, all of which must match the given Matcher.
     *
     * For infinite Streams see {@link #startsWithAllLong}
     *
     * @param matcher A Matcher against which to compare items from the Stream
     * @see #startsWithAllLong
     * @see #allMatch
     * @see #allMatchInt
     * @see #allMatchDouble
     */
    public static Matcher allMatchLong(Matcher matcher) {
        return new LongStreamAllMatches(matcher) {
            @Override
            public void describeTo(Description description) {
                description.appendText("All to match ").appendValue(matcher);
            }
        };
    }

    /**
     * A matcher for a finite Stream of primitive doubles, all of which must match the given Matcher.
     *
     * For infinite Streams see {@link #startsWithAllDouble}
     *
     * @param matcher A Matcher against which to compare items from the Stream
     * @see #startsWithAllDouble
     * @see #allMatch
     * @see #allMatchInt
     * @see #allMatchLong
     */
    public static Matcher allMatchDouble(Matcher matcher) {
        return new DoubleStreamAllMatches(matcher) {
            @Override
            public void describeTo(Description description) {
                description.appendText("All to match ").appendValue(matcher);
            }
        };
    }

    /**
     * A matcher for a finite Stream of objects, at least one of which must match the given Matcher.
     *
     * For infinite Streams see {@link #startsWithAny}
     *
     * @param matcher A Matcher against which to compare items from the Stream
     * @param  The type of items produced by the Stream
     * @see #startsWithAny
     * @see #anyMatchInt
     * @see #anyMatchLong
     * @see #anyMatchDouble
     */
    public static  Matcher> anyMatch(Matcher matcher) {
        return new StreamAnyMatches(matcher) {
            @Override
            public void describeTo(Description description) {
                description.appendText("Any to match ").appendValue(matcher);
            }
        };
    }

    /**
     * A matcher for a finite Stream of primitive longs, at least one of which must match the given Matcher.
     *
     * For infinite Streams see {@link #startsWithAnyLong}
     *
     * @param matcher A Matcher against which to compare items from the Stream
     * @see #startsWithAnyLong
     * @see #anyMatch
     * @see #anyMatchInt
     * @see #anyMatchDouble
     */
    public static Matcher anyMatchLong(Matcher matcher) {
        return new LongStreamAnyMatches(matcher) {
            @Override
            public void describeTo(Description description) {
                description.appendText("Any to match ").appendValue(matcher);
            }
        };
    }

    /**
     * A matcher for a finite Stream of primitive doubles, at least one of which must match the given Matcher.
     *
     * For infinite Streams see {@link #startsWithAnyDouble}
     *
     * @param matcher A Matcher against which to compare items from the Stream
     * @see #startsWithAnyDouble
     * @see #anyMatch
     * @see #anyMatchInt
     * @see #anyMatchDouble
     */
    public static Matcher anyMatchDouble(Matcher matcher) {
        return new DoubleStreamAnyMatches(matcher) {
            @Override
            public void describeTo(Description description) {
                description.appendText("Any to match ").appendValue(matcher);
            }
        };
    }

    /**
     * A matcher for a finite Stream of primitive ints, at least one of which must match the given Matcher.
     *
     * For infinite Streams see {@link #startsWithAnyInt}
     *
     * @param matcher A Matcher against which to compare items from the Stream
     * @see #startsWithAnyInt
     * @see #anyMatch
     * @see #anyMatchLong
     * @see #anyMatchDouble
     */

    public static Matcher anyMatchInt(Matcher matcher) {
        return new IntStreamAnyMatches(matcher) {
            @Override
            public void describeTo(Description description) {
                description.appendText("Any to match ").appendValue(matcher);
            }
        };
    }

    /**
     * A matcher for a potentially infinite Stream of objects against n expected items, matching if the first n items
     * produced by the Stream equal the expected items in order. Whether the Stream would subsequently produce
     * additional items is irrelevant.
     *
     * @param expected The expected items produced first by the Stream
     * @param  The type of items
     * @see #contains
     * @see #startsWithInt
     * @see #startsWithDouble
     * @see #startsWithLong
     */

    @SafeVarargs
    public static  Matcher> startsWith(T... expected) {
        return new BaseStreamMatcher>() {
            @Override
            protected boolean matchesSafely(Stream actual) {
                return remainingItemsEqual(new ArrayIterator<>(expected), actual.limit(expected.length).iterator());
            }
        };
    }

    /**
     * A matcher for a potentially infinite Stream of primitive doubles against n expected items, matching if the first n items
     * produced by the Stream equal the expected items in order. Whether the Stream would subsequently produce
     * additional items is irrelevant.
     *
     * @param expected The expected items produced first by the Stream
     * @see #contains
     * @see #startsWith
     * @see #startsWithInt
     * @see #startsWithLong
     */
    public static Matcher startsWithDouble(double... expected) {
        return new BaseStreamMatcher() {
            @Override
            protected boolean matchesSafely(DoubleStream actual) {
                return remainingItemsEqual(new DoubleArrayIterator(expected), actual.limit(expected.length).iterator());
            }
        };
    }

    /**
     * A matcher for a potentially infinite Stream of primitive longs against n expected items, matching if the first n items
     * produced by the Stream equal the expected items in order. Whether the Stream would subsequently produce
     * additional items is irrelevant.
     *
     * @param expected The expected items produced first by the Stream
     * @see #contains
     * @see #startsWith
     * @see #startsWithInt
     * @see #startsWithDouble
     */
    public static Matcher startsWithLong(long... expected) {
        return new BaseStreamMatcher() {
            @Override
            protected boolean matchesSafely(LongStream actual) {
                return remainingItemsEqual(new LongArrayIterator(expected), actual.limit(expected.length).iterator());
            }
        };
    }

    /**
     * A matcher for a potentially infinite Stream of primitive ints against n expected items, matching if the first n items
     * produced by the Stream equal the expected items in order. Whether the Stream would subsequently produce
     * additional items is irrelevant.
     *
     * @param expected The expected items produced first by the Stream
     * @see #contains
     * @see #startsWith
     * @see #startsWithLong
     * @see #startsWithDouble
     */
    public static Matcher startsWithInt(int... expected) {
        return new BaseStreamMatcher() {
            @Override
            protected boolean matchesSafely(IntStream actual) {
                return remainingItemsEqual(new IntArrayIterator(expected), actual.limit(expected.length).iterator());
            }
        };
    }

    private static abstract class BaseStreamMatcher> extends TypeSafeMatcher {
        final List expectedAccumulator = new LinkedList<>();
        final List actualAccumulator = new LinkedList<>();

        @Override
        public void describeTo(Description description) {
            describe(description, expectedAccumulator);
        }

        @Override
        protected void describeMismatchSafely(S item, Description description) {
            describe(description, actualAccumulator);
        }

        private void describe(Description description, List values) {
            description.appendText("Stream of ").appendValueList("[", ",", "]", values);
        }

        boolean remainingItemsEqual(Iterator expectedIterator, Iterator actualIterator) {
            if (!expectedIterator.hasNext() && !actualIterator.hasNext()) {
                return true;
            }
            if (expectedIterator.hasNext() && actualIterator.hasNext()) {
                T nextExpected = expectedIterator.next();
                expectedAccumulator.add(nextExpected);
                T nextActual = actualIterator.next();
                actualAccumulator.add(nextActual);
                if(Objects.equals(nextExpected, nextActual)) {
                    return remainingItemsEqual(expectedIterator, actualIterator);
                }
            }
            expectedIterator.forEachRemaining(expectedAccumulator::add);
            actualIterator.forEachRemaining(actualAccumulator::add);
            return false;
        }
    }

    private static void allMatchMismatch(Description mismatchDescription, long position, Object nonMatch) {
        mismatchDescription.appendText("Item ").appendText(Long.toString(position)).appendText(" failed to match: ").appendValue(nonMatch);
    }

    private static abstract class StreamAllMatches extends TypeSafeMatcher> {
        private T nonMatching;
        private long positionNonMatching = -1L;
        private final Matcher matcher;

        StreamAllMatches(Matcher matcher) {
            this.matcher = matcher;
        }

        @Override
        protected boolean matchesSafely(Stream actual) {
            return actual
                    .peek(i -> {nonMatching = i; positionNonMatching++;})
                    .allMatch(matcher::matches);
        }

        @Override
        protected void describeMismatchSafely(Stream actual, Description mismatchDescription) {
            allMatchMismatch(mismatchDescription, positionNonMatching, nonMatching);
        }
    }

    private static abstract class IntStreamAllMatches extends TypeSafeMatcher {
        private int nonMatching;
        private long positionNonMatching = -1L;
        private final Matcher matcher;

        IntStreamAllMatches(Matcher matcher) {
            this.matcher = matcher;
        }

        @Override
        protected boolean matchesSafely(IntStream actual) {
            return actual
                    .peek(i -> {nonMatching = i; positionNonMatching++;})
                    .allMatch(matcher::matches);
        }

        @Override
        protected void describeMismatchSafely(IntStream actual, Description mismatchDescription) {
            allMatchMismatch(mismatchDescription, positionNonMatching, nonMatching);
        }
    }

    private static abstract class LongStreamAllMatches extends TypeSafeMatcher {
        private long nonMatching;
        private long positionNonMatching = -1L;
        private final Matcher matcher;

        LongStreamAllMatches(Matcher matcher) {
            this.matcher = matcher;
        }

        @Override
        protected boolean matchesSafely(LongStream actual) {
            return actual
                    .peek(i -> {nonMatching = i; positionNonMatching++;})
                    .allMatch(matcher::matches);
        }

        @Override
        protected void describeMismatchSafely(LongStream actual, Description mismatchDescription) {
            allMatchMismatch(mismatchDescription, positionNonMatching, nonMatching);
        }
    }

    private static abstract class DoubleStreamAllMatches extends TypeSafeMatcher {
        private double nonMatching;
        private long positionNonMatching = -1L;
        private final Matcher matcher;

        DoubleStreamAllMatches(Matcher matcher) {
            this.matcher = matcher;
        }

        @Override
        protected boolean matchesSafely(DoubleStream actual) {
            return actual
                    .peek(i -> {nonMatching = i; positionNonMatching++;})
                    .allMatch(matcher::matches);
        }

        @Override
        protected void describeMismatchSafely(DoubleStream actual, Description mismatchDescription) {
            allMatchMismatch(mismatchDescription, positionNonMatching, nonMatching);
        }
    }

    private static void anyMatchMismatch(Description mismatchDescription, List accumulator) {
        mismatchDescription
                .appendText("None of these items matched: ")
                .appendValueList("[", ",", "]", accumulator);
    }

    private static abstract class StreamAnyMatches extends TypeSafeMatcher> {
        final List accumulator = new LinkedList<>();
        final Matcher matcher;

        StreamAnyMatches(Matcher matcher) {
            this.matcher = matcher;
        }

        @Override
        protected boolean matchesSafely(Stream actual) {
            return actual.peek(accumulator::add).anyMatch(matcher::matches);
        }

        @Override
        protected void describeMismatchSafely(Stream actual, Description mismatchDescription) {
            anyMatchMismatch(mismatchDescription,accumulator);
        }
    }

    private static abstract class LongStreamAnyMatches extends TypeSafeMatcher {
        final List accumulator = new LinkedList<>();
        final Matcher matcher;

        LongStreamAnyMatches(Matcher matcher) {
            this.matcher = matcher;
        }

        @Override
        protected boolean matchesSafely(LongStream actual) {
            return actual.peek(accumulator::add).anyMatch(matcher::matches);
        }

        @Override
        protected void describeMismatchSafely(LongStream actual, Description mismatchDescription) {
            anyMatchMismatch(mismatchDescription, accumulator);
        }
    }

    private static abstract class IntStreamAnyMatches extends TypeSafeMatcher {
        final List accumulator = new LinkedList<>();
        final Matcher matcher;

        IntStreamAnyMatches(Matcher matcher) {
            this.matcher = matcher;
        }

        @Override
        protected boolean matchesSafely(IntStream actual) {
            return actual.peek(accumulator::add).anyMatch(matcher::matches);
        }

        @Override
        protected void describeMismatchSafely(IntStream actual, Description mismatchDescription) {
            anyMatchMismatch(mismatchDescription, accumulator);
        }
    }

    private static abstract class DoubleStreamAnyMatches extends TypeSafeMatcher {
        final List accumulator = new LinkedList<>();
        final Matcher matcher;

        DoubleStreamAnyMatches(Matcher matcher) {
            this.matcher = matcher;
        }

        @Override
        protected boolean matchesSafely(DoubleStream actual) {
            return actual.peek(accumulator::add).anyMatch(matcher::matches);
        }

        @Override
        protected void describeMismatchSafely(DoubleStream actual, Description mismatchDescription) {
            anyMatchMismatch(mismatchDescription, accumulator);
        }
    }


    private static class ArrayIterator implements Iterator {
        private final T[] expected;
        private int currentPos = 0;

        @SafeVarargs
        public ArrayIterator(T... expected) {
            this.expected = expected;
        }

        @Override
        public boolean hasNext() {
            return currentPos < expected.length;
        }

        @Override
        public T next() {
            return expected[currentPos++];
        }
    }

    private static class IntArrayIterator implements PrimitiveIterator.OfInt {
        private final int[] expected;
        private int currentPos = 0;
        
        public IntArrayIterator(int... expected) {
            this.expected = expected;
        }

        @Override
        public boolean hasNext() {
            return currentPos < expected.length;
        }

        @Override
        public int nextInt() {
            return expected[currentPos++];
        }
    }

    private static class LongArrayIterator implements PrimitiveIterator.OfLong {
        private final long[] expected;
        private int currentPos = 0;

        public LongArrayIterator(long... expected) {
            this.expected = expected;
        }

        @Override
        public boolean hasNext() {
            return currentPos < expected.length;
        }

        @Override
        public long nextLong() {
            return expected[currentPos++];
        }
    }

    private static class DoubleArrayIterator implements PrimitiveIterator.OfDouble {
        private final double[] expected;
        private int currentPos = 0;

        public DoubleArrayIterator(double... expected) {
            this.expected = expected;
        }

        @Override
        public boolean hasNext() {
            return currentPos < expected.length;
        }

        @Override
        public double nextDouble() {
            return expected[currentPos++];
        }
    }
}