/** * Read whether this signal is on error and carries the cause. * * @return a boolean indicating whether this signal has an error */ default boolean hasError() { return isOnError() && getThrowable() != null; }
@Override public DefaultStepVerifier<T> expectError() { SignalEvent<T> event = new SignalEvent<>((signal, se) -> { if (!signal.isOnError()) { return errorFormatter.failOptional(se, "expected: onError(); actual: %s", signal); } else { return Optional.empty(); } }, "expectError()"); this.script.add(event); return build(); }
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; }
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(); } }
@Override public DefaultStepVerifier<T> expectError(Class<? extends Throwable> clazz) { Objects.requireNonNull(clazz, "clazz"); SignalEvent<T> event = new SignalEvent<>((signal, se) -> { if (!signal.isOnError()) { return errorFormatter.failOptional(se, "expected: onError(%s); actual: %s", clazz.getSimpleName(), signal); } else if (!clazz.isInstance(signal.getThrowable())) { return errorFormatter.failOptional(se, "expected error of type: %s; actual type: %s", clazz.getSimpleName(), signal.getThrowable()); } else { return Optional.empty(); } }, "expectError(Class)"); this.script.add(event); return build(); }
/** Cancels this subscriber if the actual signal is null or not a complete/error */ final void maybeCancel(@Nullable Signal<T> actualSignal) { if (actualSignal == null || (!actualSignal.isOnComplete() && !actualSignal.isOnError())) { cancel(); } }
final boolean onSignalCount(Signal<T> actualSignal, SignalCountEvent<T> event) { if (unasserted >= event.count) { this.script.poll(); unasserted -= event.count; } else { if (event.count != 0) { Optional<AssertionError> error = this.checkCountMismatch(event, actualSignal); if (error.isPresent()) { Exceptions.addThrowable(ERRORS, this, error.get()); if(actualSignal.isOnError()) { // #55 ensure the onError is added as a suppressed to the AssertionError error.get().addSuppressed(actualSignal.getThrowable()); } maybeCancel(actualSignal); this.completeLatch.countDown(); } } return true; } return false; }
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; }
@Override public DefaultStepVerifier<T> expectErrorMatches(Predicate<Throwable> predicate) { Objects.requireNonNull(predicate, "predicate"); SignalEvent<T> event = new SignalEvent<>((signal, se) -> { if (!signal.isOnError()) { return errorFormatter.failOptional(se, "expected: onError(); actual: %s", signal); } else if (!predicate.test(signal.getThrowable())) { return errorFormatter.failOptional(se, "predicate failed on exception: %s", signal.getThrowable()); } else { return Optional.empty(); } }, "expectErrorMatches"); this.script.add(event); return build(); }
@Override public DefaultStepVerifier<T> expectErrorMessage(String errorMessage) { SignalEvent<T> event = new SignalEvent<>((signal, se) -> { if (!signal.isOnError()) { return errorFormatter.failOptional(se, "expected: onError(\"%s\"); actual: %s", errorMessage, signal); } else if (!Objects.equals(errorMessage, signal.getThrowable() .getMessage())) { return errorFormatter.failOptional(se, "expected error message: \"%s\"; " + "actual " + "message: %s", errorMessage, signal.getThrowable() .getMessage()); } else { return Optional.empty(); } }, "expectErrorMessage"); this.script.add(event); return build(); }
private DefaultStepVerifier<T> consumeErrorWith(Consumer<Throwable> assertionConsumer, String description, boolean wrap) { Objects.requireNonNull(assertionConsumer, "assertionConsumer"); SignalEvent<T> event = new SignalEvent<>((signal, se) -> { if (!signal.isOnError()) { return errorFormatter.failOptional(se, "expected: onError(); actual: %s", signal); } else { try { assertionConsumer.accept(signal.getThrowable()); return Optional.empty(); } catch (AssertionError e) { if (wrap) return errorFormatter.failOptional(se, "assertion failed on exception <%s>: %s", signal.getThrowable(), e.getMessage()); throw e; } } }, description); this.script.add(event); return build(); }
private <T> Predicate<? super Signal<T>> signalErrorMessage(String expectedMessage) { return signal -> signal.isOnError() && signal.getThrowable() != null && expectedMessage.equals(signal.getThrowable().getMessage()); }
@Test public void testDoOnEachSignalWithError() { List<Signal<Integer>> signals = new ArrayList<>(4); Flux<Integer> flux = Flux.<Integer>error(new IllegalArgumentException("foo")) .doOnEach(signals::add); StepVerifier.create(flux) .expectSubscription() .expectErrorMessage("foo") .verify(); assertThat(signals.size(), is(1)); assertTrue("onError expected", signals.get(0).isOnError()); assertThat("plain exception expected", signals.get(0).getThrowable().getMessage(), is("foo")); }
@Test public void testDoOnEachSignalWithError() { List<Signal<Integer>> signals = new ArrayList<>(4); Mono<Integer> mono = Mono.<Integer>error(new IllegalArgumentException("foo")) .doOnEach(signals::add); StepVerifier.create(mono) .expectSubscription() .expectErrorMessage("foo") .verify(); assertThat(signals.size(), is(1)); assertTrue("onError expected", signals.get(0).isOnError()); assertThat("plain exception expected", signals.get(0).getThrowable().getMessage(), is("foo")); }
@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 never() { AssertSubscriber<Integer> ts = AssertSubscriber.create(); AtomicInteger onNext = new AtomicInteger(); AtomicReference<Throwable> onError = new AtomicReference<>(); AtomicBoolean onComplete = new AtomicBoolean(); Mono.<Integer>never() .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()).isFalse(); }
@Test public void testDoOnEachSignalWithError() throws InterruptedException { List<Signal<Integer>> signals = Collections.synchronizedList(new ArrayList<>(4)); ParallelFlux<Integer> flux = Flux.<Integer>error(new IllegalArgumentException("boom")).parallel(2) .runOn(Schedulers.parallel()) .doOnEach(signals::add); //we use a lambda subscriber and latch to avoid using `sequential` CountDownLatch latch = new CountDownLatch(2); flux.subscribe(v -> { }, e -> latch.countDown(), latch::countDown); assertTrue(latch.await(2, TimeUnit.SECONDS)); assertThat(signals).hasSize(2); assertTrue("rail 1 onError expected", signals.get(0) .isOnError()); assertTrue("rail 2 onError expected", signals.get(1) .isOnError()); assertThat(signals.get(0).getThrowable()).as("plain exception rail 1 expected") .hasMessage("boom"); assertThat(signals.get(1).getThrowable()).as("plain exception rail 2 expected") .hasMessage("boom"); }
/** * 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 @Parameters(method = "sourcesError") public void errorCallbackError(Flux<Integer> source) { AssertSubscriber<Integer> ts = AssertSubscriber.create(); LongAdder state = new LongAdder(); IllegalStateException err = new IllegalStateException("test"); source .doOnEach(s -> { if (s.isOnError()) { state.increment(); throw Exceptions.propagate(err); } }) .filter(t -> true) .subscribe(ts); ts.assertNoValues(); ts.assertError(IllegalStateException.class); ts.assertErrorWith(e -> e.getSuppressed()[0].getMessage().equals(sourceErrorMessage)); ts.assertErrorWith(e -> e.getMessage().equals("test")); Assert.assertEquals(1, state.intValue()); }