/** * Has this signal an item associated with it ? (which only happens if it is an * (onNext) signal) * * @return a boolean indicating whether or not this signal has an item associated with * it */ default boolean hasValue() { return isOnNext() && get() != null; }
final Optional<AssertionError> checkCountMismatch(SignalCountEvent<T> event, Signal<T> s) { long expected = event.count; if (!s.isOnNext()) { return errorFormatter.failOptional(event, "expected: count = %s; actual: counted = %s; signal: %s", expected, unasserted, s); } else { return Optional.empty(); } }
/** Returns true if the requested amount was overflown by the given signal */ final boolean checkRequestOverflow(Signal<T> s) { long r = requested; if (!s.isOnNext() || r < 0 || r == Long.MAX_VALUE //was Long.MAX from beginning or switched to unbounded || (establishedFusionMode == Fuseable.ASYNC && r != 0L) || r >= produced) { return false; } else { //not really an expectation failure so customize the message setFailurePrefix("request overflow (", s, "expected production of at most %s; produced: %s; request overflown by signal: %s", r, produced, s); return true; } }
MonoCacheTime(Mono<? extends T> source, Function<? super T, Duration> valueTtlGenerator, Function<Throwable, Duration> errorTtlGenerator, Supplier<Duration> emptyTtlGenerator, Scheduler clock) { super(source); this.ttlGenerator = sig -> { if (sig.isOnNext()) return valueTtlGenerator.apply(sig.get()); if (sig.isOnError()) return errorTtlGenerator.apply(sig.getThrowable()); return emptyTtlGenerator.get(); }; this.clock = clock; @SuppressWarnings("unchecked") Signal<T> emptyState = (Signal<T>) EMPTY; this.state = emptyState; }
private DefaultStepVerifierBuilder<T> consumeNextWith(Consumer<? super T> consumer, String description) { Objects.requireNonNull(consumer, "consumer"); checkPotentialHang(1, description); SignalEvent<T> event = new SignalEvent<>((signal, se) -> { if (!signal.isOnNext()) { return errorFormatter.failOptional(se, "expected: onNext(); actual: %s", signal); } else { consumer.accept(signal.get()); return Optional.empty(); } }, description); this.script.add(event); return this; }
Optional<AssertionError> test(Signal<T> signal, Iterator<? extends T> iterator) { if (signal.isOnNext()) { if (!iterator.hasNext()) { return Optional.empty(); } T d2 = iterator.next(); if (!Objects.equals(signal.get(), d2)) { return errorFormatter.failOptional(this, "expected : onNext(%s); actual: %s; iterable: %s", d2, signal.get(), iterable); } return iterator.hasNext() ? EXPECT_MORE : Optional.empty(); } if (iterator.hasNext() || signal.isOnError()) { return errorFormatter.failOptional(this, "expected next value: %s; actual signal: %s; iterable: %s", iterator.hasNext() ? iterator.next() : "none", signal, iterable); } return Optional.empty(); } }
boolean onSignal(Signal<T> actualSignal) { SignalEvent<T> signalEvent = (SignalEvent<T>) this.script.poll(); Optional<AssertionError> error = signalEvent.test(actualSignal); if (error.isPresent()) { Exceptions.addThrowable(ERRORS, this, error.get()); // #55 ensure the onError is added as a suppressed to the AssertionError if(actualSignal.isOnError()) { error.get().addSuppressed(actualSignal.getThrowable()); } maybeCancel(actualSignal); this.completeLatch.countDown(); return true; } if (actualSignal.isOnNext()) { unasserted--; } return false; }
private void addExpectedValue(T value) { String desc = String.format("expectNext(%s)", value); checkPotentialHang(1, desc); SignalEvent<T> event = new SignalEvent<>((signal, se) -> { if (!signal.isOnNext()) { return errorFormatter.failOptional(se, "expected: onNext(%s); actual: %s", value, signal); } else if (!Objects.equals(value, signal.get())) { return errorFormatter.failOptional(se, "expected value: %s; actual value: %s", value, signal.get()); } else { return Optional.empty(); } }, desc); this.script.add(event); }
@Override public DefaultStepVerifierBuilder<T> expectNextMatches( Predicate<? super T> predicate) { Objects.requireNonNull(predicate, "predicate"); checkPotentialHang(1, "expectNextMatches"); SignalEvent<T> event = new SignalEvent<>((signal, se) -> { if (!signal.isOnNext()) { return errorFormatter.failOptional(se, "expected: onNext(); actual: %s", signal); } else if (!predicate.test(signal.get())) { return errorFormatter.failOptional(se, "predicate failed on value: %s", signal.get()); } else { return Optional.empty(); } }, "expectNextMatches"); this.script.add(event); return this; }
boolean consumeWhile(Signal<T> actualSignal, SignalConsumeWhileEvent<T> whileEvent) { if (actualSignal.isOnNext()) { if (whileEvent.test(actualSignal.get())) { //the value matches, gobble it up unasserted--; if (this.logger != null) { logger.debug("{} consumed {}", whileEvent.getDescription(), actualSignal); } return true; } } if (this.logger != null) { logger.debug("{} stopped at {}", whileEvent.getDescription(), actualSignal); } //stop evaluating the predicate this.script.poll(); return false; }
@Test public void empty() { AssertSubscriber<Integer> ts = AssertSubscriber.create(); AtomicInteger onNext = new AtomicInteger(); AtomicReference<Throwable> onError = new AtomicReference<>(); AtomicBoolean onComplete = new AtomicBoolean(); Mono.<Integer>empty() .doOnEach(s -> { if (s.isOnNext()) { onNext.incrementAndGet(); } else if (s.isOnError()) { onError.set(s.getThrowable()); } else if (s.isOnComplete()) { onComplete.set(true); } }) .subscribe(ts); assertThat(onNext.get()).isZero(); assertThat(onError.get()).isNull(); assertThat(onComplete.get()).isTrue(); }
@Test public void materialize() { StepVerifier.create(Flux.just("Three", "Two", "One") .materialize()) .expectNextMatches(s -> s.isOnNext() && "Three".equals(s.get())) .expectNextMatches(s -> s.isOnNext() && "Two".equals(s.get())) .expectNextMatches(s -> s.isOnNext() && "One".equals(s.get())) .expectNextMatches(Signal::isOnComplete) .verifyComplete(); }
@Test public void testDoOnEachSignal() { List<Signal<Integer>> signals = new ArrayList<>(4); List<Integer> values = new ArrayList<>(2); Flux<Integer> flux = Flux.just(1, 2) .doOnEach(signals::add) .doOnEach(s -> { if (s.isOnNext()) values.add(s.get()); }); StepVerifier.create(flux) .expectSubscription() .expectNext(1, 2) .expectComplete() .verify(); assertThat(signals.size(), is(3)); assertThat("onNext signal are not reused", signals.get(0).get(), is(2)); assertThat("onNext signal isn't last value", signals.get(1).get(), is(2)); assertTrue("onComplete expected", signals.get(2).isOnComplete()); assertThat("1st onNext value unexpected", values.get(0), is(1)); assertThat("2nd onNext value unexpected", values.get(1), is(2)); }
@Test public void materialize() { StepVerifier.create(Mono.just("Three") .materialize()) .expectNextMatches(s -> s.isOnNext() && s.get() .equals("Three")) .verifyComplete(); }
@Test public void consumerError() { LongAdder state = new LongAdder(); Throwable err = new Exception("test"); StepVerifier.create( Mono.just(1) .doOnEach(s -> { if (s.isOnNext()) { state.increment(); throw Exceptions.propagate(err); } })) .expectErrorMessage("test") .verify(); assertThat(state.intValue()).isEqualTo(1); }
/** * Propagate the signal represented by this {@link Signal} instance to a * given {@link Subscriber}. * * @param observer the {@link Subscriber} to play the {@link Signal} on */ @Override default void accept(Subscriber<? super T> observer) { if (isOnNext()) { observer.onNext(get()); } else if (isOnComplete()) { observer.onComplete(); } else if (isOnError()) { observer.onError(getThrowable()); } else if (isOnSubscribe()) { observer.onSubscribe(getSubscription()); } } }
@Test public void materialize2() { StepVerifier.create(Flux.just("Three", "Two") .concatWith(Flux.error(new RuntimeException("test"))) .materialize()) .expectNextMatches(s -> s.isOnNext() && "Three".equals(s.get())) .expectNextMatches(s -> s.isOnNext() && "Two".equals(s.get())) .expectNextMatches(s -> s.isOnError() && s.getThrowable() != null && "test".equals(s.getThrowable().getMessage())) .verifyComplete(); }
@Test public void consumerBubbleError() { LongAdder state = new LongAdder(); Throwable err = new Exception("test"); assertThatThrownBy(() -> StepVerifier.create( Mono.just(1) .doOnEach(s -> { if (s.isOnNext()) { state.increment(); throw Exceptions.bubble(err); } })) .expectErrorMessage("test") .verify()) .isInstanceOf(RuntimeException.class) .matches(Exceptions::isBubbling, "bubbling") .hasCause(err); //equivalent to unwrap for this case assertThat(state.intValue()).isEqualTo(1); }
@Test public void fusedAsyncCallbackErrorsOnTerminal() { AtomicReference<String> onNext = new AtomicReference<>(); AtomicReference<Throwable> onError = new AtomicReference<>(); AtomicBoolean onComplete = new AtomicBoolean(); LongAdder state = new LongAdder(); StepVerifier.create(Flux.just("foo") .publishOn(Schedulers.immediate()) .map(s -> s + "_async") .doOnEach(s -> { if (s.isOnNext()) { onNext.set(s.get()); } else { throw new IllegalStateException("boom"); } })) .expectFusion(Fuseable.ASYNC, Fuseable.ASYNC) .expectNext("foo_async") .verifyErrorMessage("boom"); assertThat(onNext).hasValue("foo_async"); assertThat(onError.get()).isNull(); assertThat(onComplete).isFalse(); }
@Test @Parameters(method = "sources12Complete") public void nextCallbackError(Flux<Integer> source) { AssertSubscriber<Integer> ts = AssertSubscriber.create(); LongAdder state = new LongAdder(); Throwable err = new Exception("test"); source .doOnEach(s -> { if (s.isOnNext()) { state.increment(); throw Exceptions.propagate(err); } }) .filter(t -> true) .subscribe(ts); //nominal error path (DownstreamException) ts.assertErrorMessage("test"); Assert.assertEquals(1, state.intValue()); }