@Test public void virtualTimeSchedulerUseExactlySupplied() { VirtualTimeScheduler vts1 = VirtualTimeScheduler.create(); VirtualTimeScheduler vts2 = VirtualTimeScheduler.create(); VirtualTimeScheduler.getOrSet(vts1); StepVerifier.withVirtualTime(Mono::empty, () -> vts2, Long.MAX_VALUE) .then(() -> assertThat(VirtualTimeScheduler.get()).isSameAs(vts2)) .verifyComplete(); assertThat(vts1.isDisposed()).isFalse(); assertThat(vts2.isDisposed()).isTrue(); assertThat(VirtualTimeScheduler.isFactoryEnabled()).isFalse(); }
@Test public void apiTakeSchedulerShortcircuits() { VirtualTimeScheduler vts = VirtualTimeScheduler.create(); StepVerifier.create( Mono.delay(Duration.ofMillis(200)) .take(Duration.ofSeconds(10), vts) ) .then(() -> vts.advanceTimeBy(Duration.ofSeconds(10))) .verifyComplete(); }
@Test public void monoApiTestMillisAndTimer() { VirtualTimeScheduler vts = VirtualTimeScheduler.create(); StepVerifier.withVirtualTime( () -> Mono.just("foo").delayElement(Duration.ofMillis(5000L), vts), () -> vts, Long.MAX_VALUE) .expectSubscription() .expectNoEvent(Duration.ofSeconds(5)) .expectNext("foo") .verifyComplete(); }
@Test public void cancelBeforeNext() { VirtualTimeScheduler vts = VirtualTimeScheduler.create(); AtomicBoolean emitted = new AtomicBoolean(); AtomicBoolean cancelled = new AtomicBoolean(); Mono<Long> source = Mono.delay(Duration.ofMillis(1000), vts); StepVerifier.withVirtualTime( () -> new MonoDelayElement<>(source, 2, TimeUnit.SECONDS, vts) .doOnCancel(() -> cancelled.set(true)) .doOnNext(n -> emitted.set(true)), () -> vts, Long.MAX_VALUE) .expectSubscription() .expectNoEvent(Duration.ofMillis(500)) .thenCancel() .verify(); vts.advanceTimeBy(Duration.ofHours(1)); assertThat(emitted.get()).isFalse(); assertThat(cancelled.get()).isTrue(); }
@Test public void nestedSchedule() { VirtualTimeScheduler vts = VirtualTimeScheduler.create(); List<Long> singleExecutionsTimestamps = new ArrayList<>(); try { vts.schedule(() -> vts.schedule( () -> singleExecutionsTimestamps.add(vts.now(TimeUnit.MILLISECONDS)), 100, TimeUnit.MILLISECONDS ), 300, TimeUnit.MILLISECONDS); vts.advanceTimeBy(Duration.ofMillis(1000)); assertThat(singleExecutionsTimestamps) .as("single executions") .containsExactly(400L); } finally { vts.dispose(); } }
@Test public void cancelDuringDelay() { VirtualTimeScheduler vts = VirtualTimeScheduler.create(); AtomicBoolean emitted = new AtomicBoolean(); AtomicBoolean cancelled = new AtomicBoolean(); Mono<String> source = Mono.just("foo").log().hide(); StepVerifier.withVirtualTime( () -> new MonoDelayElement<>(source, 2, TimeUnit.SECONDS, vts) .doOnCancel(() -> cancelled.set(true)) .log() .doOnNext(n -> emitted.set(true)), () -> vts, Long.MAX_VALUE) .expectSubscription() .expectNoEvent(Duration.ofSeconds(1)) .thenCancel() .verify(); vts.advanceTimeBy(Duration.ofHours(1)); assertThat(emitted.get()).isFalse(); assertThat(cancelled.get()).isTrue(); }
@Test public void cancelUpstreamOnceWhenCancelled() { VirtualTimeScheduler vts = VirtualTimeScheduler.create(); AtomicLong upstreamCancelCount = new AtomicLong(); Mono<String> source = Mono.just("foo").log().hide() .doOnCancel(() -> upstreamCancelCount.incrementAndGet()); StepVerifier.withVirtualTime( () -> new MonoDelayElement<>(source, 2, TimeUnit.SECONDS, vts), () -> vts, Long.MAX_VALUE) .expectSubscription() .expectNoEvent(Duration.ofSeconds(1)) .thenCancel() .verify(); vts.advanceTimeBy(Duration.ofHours(1)); assertThat(upstreamCancelCount.get()).isEqualTo(1); }
@Test public void disposedSchedulerIsStillCleanedUp() { VirtualTimeScheduler vts = VirtualTimeScheduler.create(); vts.dispose(); assertThat(VirtualTimeScheduler.isFactoryEnabled()).isFalse(); StepVerifier.withVirtualTime(() -> Mono.just("foo"), () -> vts, Long.MAX_VALUE) .then(() -> assertThat(VirtualTimeScheduler.isFactoryEnabled()).isTrue()) .then(() -> assertThat(VirtualTimeScheduler.get()).isSameAs(vts)) .expectNext("foo") .verifyComplete(); assertThat(VirtualTimeScheduler.isFactoryEnabled()).isFalse(); StepVerifier.withVirtualTime(() -> Mono.just("foo")) .then(() -> assertThat(VirtualTimeScheduler.isFactoryEnabled()).isTrue()) .then(() -> assertThat(VirtualTimeScheduler.get()).isNotSameAs(vts)) .expectNext("foo") .verifyComplete(); assertThat(VirtualTimeScheduler.isFactoryEnabled()).isFalse(); }
@Test public void enableTwoSimilarSchedulersUsesFirst() { VirtualTimeScheduler vts1 = VirtualTimeScheduler.create(); VirtualTimeScheduler vts2 = VirtualTimeScheduler.create(); VirtualTimeScheduler firstEnableResult = VirtualTimeScheduler.getOrSet(vts1); VirtualTimeScheduler secondEnableResult = VirtualTimeScheduler.getOrSet(vts2); Assert.assertSame(vts1, firstEnableResult); Assert.assertSame(vts1, secondEnableResult); Assert.assertSame(vts1, uncache(Schedulers.single())); Assert.assertFalse(vts1.shutdown); }
@Test public void expireAfterTtlNormal() { VirtualTimeScheduler vts = VirtualTimeScheduler.create(); AtomicInteger subCount = new AtomicInteger(); Mono<Integer> source = Mono.defer(() -> Mono.just(subCount.incrementAndGet())); Mono<Integer> cached = source.cache(Duration.ofMillis(100), vts) .hide(); StepVerifier.create(cached) .expectNoFusionSupport() .expectNext(1) .as("first subscription caches 1") .verifyComplete(); vts.advanceTimeBy(Duration.ofMillis(110)); StepVerifier.create(cached) .expectNext(2) .as("cached value should expire") .verifyComplete(); assertThat(subCount.get()).isEqualTo(2); }
@Test public void cancelUpstreamOnceWhenRejected() { VirtualTimeScheduler vts = VirtualTimeScheduler.create(); vts.dispose(); AtomicLong upstreamCancelCount = new AtomicLong(); Mono<String> source = Mono.just("foo").log().hide() .doOnCancel(upstreamCancelCount::incrementAndGet); try { StepVerifier.withVirtualTime( () -> new MonoDelayElement<>(source, 2, TimeUnit.SECONDS, vts).log(), () -> vts, Long.MAX_VALUE) .expectSubscription() .verifyComplete(); } catch (Throwable e) { assertThat(e).hasMessageContaining("Scheduler unavailable"); } finally { assertThat(upstreamCancelCount.get()).isEqualTo(1); } }
@Test public void expireAfterTtlConditional() { VirtualTimeScheduler vts = VirtualTimeScheduler.create(); AtomicInteger subCount = new AtomicInteger(); Mono<Integer> source = Mono.defer(() -> Mono.just(subCount.incrementAndGet())); Mono<Integer> cached = source.cache(Duration.ofMillis(100), vts) .hide() .filter(always -> true); StepVerifier.create(cached) .expectNoFusionSupport() .expectNext(1) .as("first subscription caches 1") .verifyComplete(); vts.advanceTimeBy(Duration.ofMillis(110)); StepVerifier.create(cached) .expectNext(2) .as("cached value should expire") .verifyComplete(); assertThat(subCount.get()).isEqualTo(2); }
@Test(timeout = 3000) public void verifyVirtualTimeOnNextIntervalManual() { VirtualTimeScheduler vts = VirtualTimeScheduler.create(); StepVerifier.withVirtualTime(() -> Flux.interval(Duration.ofMillis(1000), vts) .map(d -> "t" + d)) .then(() -> vts.advanceTimeBy(Duration.ofHours(1))) .expectNextCount(3600) .thenCancel() .verify(); }
@Test public void suppliedVirtualTimeButNoSourceDoesntEnableScheduler() { VirtualTimeScheduler vts = VirtualTimeScheduler.create(); new DefaultStepVerifierBuilder<String>(StepVerifierOptions.create() .initialRequest(Long.MAX_VALUE) .virtualTimeSchedulerSupplier(() -> vts), null) //important to avoid triggering of vts capture-and-enable .expectNoEvent(Duration.ofSeconds(4)) .expectComplete() .toSubscriber(); try { //also test the side effect case where VTS has been enabled and not reset VirtualTimeScheduler current = VirtualTimeScheduler.get(); assertThat(current).isNotSameAs(vts); } catch (IllegalStateException e) { assertThat(e).hasMessageContaining("VirtualTimeScheduler"); } }
@Test public void cacheFluxTTL2() { VirtualTimeScheduler vts = VirtualTimeScheduler.create(); AtomicInteger i = new AtomicInteger(0); Flux<Integer> source = Flux.defer(() -> Flux.just(i.incrementAndGet())) .cache(Duration.ofMillis(2000), vts); StepVerifier.create(source) .expectNext(1) .verifyComplete(); StepVerifier.create(source) .expectNext(1) .verifyComplete(); vts.advanceTimeBy(Duration.ofSeconds(3)); StepVerifier.create(source) .expectNext(2) .verifyComplete(); }
@Test public void verifyVirtualTimeOnErrorAsync() { VirtualTimeScheduler vts = VirtualTimeScheduler.create(); StepVerifier.withVirtualTime(() -> Flux.just(123) .subscribeOn(vts), () -> vts, 0) .thenRequest(1) .thenAwait() .expectNext(123) .expectComplete() .verify(); }
@Test public void enableProvidedAllSchedulerIdempotent() { VirtualTimeScheduler vts = VirtualTimeScheduler.create(); VirtualTimeScheduler.getOrSet(vts); Assert.assertSame(vts, uncache(Schedulers.single())); Assert.assertFalse(vts.shutdown); VirtualTimeScheduler.getOrSet(vts); Assert.assertSame(vts, uncache(Schedulers.single())); Assert.assertFalse(vts.shutdown); }
@Test public void shouldNotRetainSubscriptionToSourceWhenComplete() throws Exception { VirtualTimeScheduler scheduler = VirtualTimeScheduler.create(); Duration gracePeriod = Duration.ofMillis(10); Flux<String> f = Flux.just("hello world") .replay(1) .refCount(1, gracePeriod, scheduler); AssertSubscriber<String> s = AssertSubscriber.create(); f.subscribe(s); scheduler.advanceTimeBy(gracePeriod); StepVerifier.create(f.next()) .expectNext("hello world") .verifyComplete(); scheduler.advanceTimeBy(gracePeriod); s.assertValueCount(1).assertNoError().assertComplete(); }
@Test public void cacheFluxTTL() { VirtualTimeScheduler vts = VirtualTimeScheduler.create(); Flux<Tuple2<Long, Integer>> source = Flux.just(1, 2, 3) .delayElements(Duration.ofMillis(1000) , vts) .cache(Duration.ofMillis(2000), vts) .elapsed(vts); StepVerifier.withVirtualTime(() -> source, () -> vts, Long.MAX_VALUE) .thenAwait(Duration.ofSeconds(3)) .expectNextMatches(t -> t.getT1() == 1000 && t.getT2() == 1) .expectNextMatches(t -> t.getT1() == 1000 && t.getT2() == 2) .expectNextMatches(t -> t.getT1() == 1000 && t.getT2() == 3) .verifyComplete(); StepVerifier.withVirtualTime(() -> source, () -> vts, Long.MAX_VALUE) .thenAwait(Duration.ofSeconds(3)) .expectNextMatches(t -> t.getT1() == 0 && t.getT2() == 2) .expectNextMatches(t -> t.getT1() == 0 && t.getT2() == 3) .verifyComplete(); }
@Test public void cacheFluxHistoryTTL() { VirtualTimeScheduler vts = VirtualTimeScheduler.create(); Flux<Tuple2<Long, Integer>> source = Flux.just(1, 2, 3) .delayElements(Duration.ofMillis (1000), vts) .cache(2, Duration.ofMillis(2000), vts) .elapsed(vts); StepVerifier.withVirtualTime(() -> source, () -> vts, Long.MAX_VALUE) .thenAwait(Duration.ofSeconds(3)) .expectNextMatches(t -> t.getT1() == 1000 && t.getT2() == 1) .expectNextMatches(t -> t.getT1() == 1000 && t.getT2() == 2) .expectNextMatches(t -> t.getT1() == 1000 && t.getT2() == 3) .verifyComplete(); StepVerifier.withVirtualTime(() -> source, () -> vts, Long.MAX_VALUE) .thenAwait(Duration.ofSeconds(3)) .expectNextMatches(t -> t.getT1() == 0 && t.getT2() == 2) .expectNextMatches(t -> t.getT1() == 0 && t.getT2() == 3) .verifyComplete(); }