/*
* Copyright (c) 2011-2017 Pivotal Software Inc, All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package reactor.core.publisher;
import java.time.Duration;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
import org.junit.Test;
import org.reactivestreams.Subscription;
import reactor.core.Scannable;
import reactor.test.StepVerifier;
import static org.assertj.core.api.Assertions.assertThat;
public class FluxFilterWhenTest {
@Test
public void normal() {
StepVerifier.withVirtualTime(() -> Flux.range(1, 10)
.filterWhen(v -> Mono.just(v % 2 == 0)
.delayElement(Duration.ofMillis(100))))
.thenAwait(Duration.ofSeconds(5))
.expectNext(2, 4, 6, 8, 10)
.verifyComplete();
}
@Test
public void normalSync() {
StepVerifier.create(Flux.range(1, 10)
.filterWhen(v -> Mono.just(v % 2 == 0).hide()))
.expectNext(2, 4, 6, 8, 10)
.verifyComplete();
}
@Test
public void normalSyncFused() {
StepVerifier.create(Flux.range(1, 10)
.filterWhen(v -> Mono.just(v % 2 == 0)))
.expectNext(2, 4, 6, 8, 10)
.verifyComplete();
}
@Test
public void allEmpty() {
StepVerifier.create(Flux.range(1, 10)
.filterWhen(v -> Mono.<Boolean>empty().hide()))
.verifyComplete();
}
@Test
public void allEmptyFused() {
StepVerifier.create(Flux.range(1, 10)
.filterWhen(v -> Mono.empty()))
.verifyComplete();
}
@Test
public void empty() {
StepVerifier.create(Flux.<Integer>empty()
.filterWhen(v -> Mono.just(true)))
.verifyComplete();
}
@Test
public void emptyBackpressured() {
StepVerifier.create(Flux.<Integer>empty()
.filterWhen(v -> Mono.just(true)), 0L)
.verifyComplete();
}
@Test
public void error() {
StepVerifier.create(Flux.<Integer>error(new IllegalStateException())
.filterWhen(v -> Mono.just(true)))
.verifyError(IllegalStateException.class);
}
@Test
public void errorBackpressured() {
StepVerifier.create(Flux.<Integer>error(new IllegalStateException())
.filterWhen(v -> Mono.just(true)), 0L)
.verifyError(IllegalStateException.class);
}
@Test
public void backpressureExactlyOne() {
StepVerifier.create(Flux.just(1)
.filterWhen(v -> Mono.just(true)), 1L)
.expectNext(1)
.verifyComplete();
}
@Test
public void longSourceSingleStep() {
StepVerifier.create(Flux.range(1, 1000)
.filterWhen(v -> Flux.just(true).limitRate(1)))
.expectNextCount(1000)
.verifyComplete();
}
@Test
public void longSource() {
StepVerifier.create(Flux.range(1, 1000)
.filterWhen(v -> Mono.just(true).hide()))
.expectNextCount(1000)
.verifyComplete();
}
@Test
public void longSourceFused() {
StepVerifier.create(Flux.range(1, 1000)
.filterWhen(v -> Mono.just(true)))
.expectNextCount(1000)
.verifyComplete();
}
@Test
public void oneAndErrorInner() {
StepVerifier.create(Flux.just(1)
.filterWhen(v -> s -> {
s.onSubscribe(Operators.emptySubscription());
s.onNext(true);
s.onError(new IllegalStateException());
},16))
.expectNext(1)
.expectComplete()
.verifyThenAssertThat()
.hasDroppedErrorsSatisfying(
c -> assertThat(c)
.hasSize(1)
.element(0).isInstanceOf(IllegalStateException.class)
);
}
@Test
public void predicateThrows() {
StepVerifier.create(Flux.just(1)
.filterWhen(v -> { throw new IllegalStateException(); }))
.expectError(IllegalStateException.class);
}
@Test
public void predicateNull() {
StepVerifier.create(Flux.just(1).filterWhen(v -> null))
.verifyError(NullPointerException.class);
}
@Test
public void predicateError() {
StepVerifier.create(Flux.just(1)
.filterWhen(v -> Mono.<Boolean>error(new IllegalStateException()).hide()))
.verifyError(IllegalStateException.class);
}
@Test
public void predicateErrorFused() {
StepVerifier.create(Flux.just(1)
.filterWhen(v -> Mono.fromCallable(() -> { throw new IllegalStateException(); })))
.verifyError(IllegalStateException.class);
}
@Test
public void take() {
StepVerifier.create(Flux.range(1, 10)
.filterWhen(v -> Mono.just(v % 2 == 0).hide())
.take(1))
.expectNext(2)
.verifyComplete();
}
@Test
public void take1Cancel() {
AtomicLong onNextCount = new AtomicLong();
AtomicReference<SignalType> endSignal = new AtomicReference<>();
BaseSubscriber<Object> bs = new BaseSubscriber<Object>() {
@Override
protected void hookOnSubscribe(Subscription subscription) {
requestUnbounded();
}
@Override
public void hookOnNext(Object t) {
onNextCount.incrementAndGet();
cancel();
onComplete();
}
@Override
protected void hookFinally(SignalType type) {
endSignal.set(type);
}
};
Flux.range(1, 1000)
.filterWhen(v -> Mono.just(true).hide())
.subscribe(bs);
assertThat(onNextCount.get()).isEqualTo(1);
assertThat(endSignal.get()).isEqualTo(SignalType.CANCEL);
}
@Test
public void take1CancelBackpressured() {
AtomicLong onNextCount = new AtomicLong();
AtomicReference<SignalType> endSignal = new AtomicReference<>();
BaseSubscriber<Object> bs = new BaseSubscriber<Object>() {
@Override
protected void hookOnSubscribe(Subscription subscription) {
request(1);
}
@Override
public void hookOnNext(Object t) {
onNextCount.incrementAndGet();
cancel();
onComplete();
}
@Override
protected void hookFinally(SignalType type) {
endSignal.set(type);
}
};
Flux.range(1, 1000)
.filterWhen(v -> Mono.just(true).hide())
.subscribe(bs);
assertThat(onNextCount.get()).isEqualTo(1);
assertThat(endSignal.get()).isEqualTo(SignalType.CANCEL);
}
@Test
public void cancel() {
final EmitterProcessor<Boolean> pp = EmitterProcessor.create();
StepVerifier.create(Flux.range(1, 5)
.filterWhen(v -> pp, 16))
.thenCancel();
assertThat(pp.hasDownstreams()).isFalse();
}
@Test
public void innerFluxCancelled() {
AtomicInteger cancelCount = new AtomicInteger();
StepVerifier.create(Flux.range(1, 3)
.filterWhen(v -> Flux.just(true, false, false)
.doOnCancel(cancelCount::incrementAndGet)))
.expectNext(1, 2, 3)
.verifyComplete();
assertThat(cancelCount.get()).isEqualTo(3);
}
@Test
public void innerFluxOnlyConsidersFirstValue() {
StepVerifier.create(Flux.range(1, 3)
.filterWhen(v -> Flux.just(false, true, true)))
.verifyComplete();
}
@Test
public void innerMonoNotCancelled() {
AtomicInteger cancelCount = new AtomicInteger();
StepVerifier.create(Flux.range(1, 3)
.filterWhen(v -> Mono.just(true)
.doOnCancel(cancelCount::incrementAndGet)))
.expectNext(1, 2, 3)
.verifyComplete();
assertThat(cancelCount.get()).isEqualTo(0);
}
@Test
public void bufferSizeIsAlsoPrefetch() {
AtomicLong requested = new AtomicLong();
Flux.range(1, 10)
.hide()
.doOnRequest(r -> requested.compareAndSet(0, r))
.filterWhen(v -> Mono.just(v % 2 == 0), 5)
.subscribe().dispose();
assertThat(requested.get()).isEqualTo(5);
}
@Test
public void largeBufferSize() {
int bufferSize = 65536; //the buffer size given as example in javadoc
AtomicLong requested = new AtomicLong();
Flux.range(1, 10)
.hide()
.doOnRequest(r -> requested.compareAndSet(0, r))
.filterWhen(v -> Mono.just(v % 2 == 0), bufferSize)
.subscribe().dispose();
assertThat(requested.get()).isEqualTo(bufferSize);
bufferSize = bufferSize + 1; //assert even if above it is still fine
requested.set(0);
Flux.range(1, 10)
.hide()
.doOnRequest(r -> requested.compareAndSet(0, r))
.filterWhen(v -> Mono.just(v % 2 == 0), bufferSize)
.subscribe().dispose();
assertThat(requested.get()).isEqualTo(bufferSize);
}
@Test
public void introspectionNormal() {
AtomicReference<Scannable> scannable = new AtomicReference<>();
Flux<Integer> flux = Flux.range(1, 10)
.filterWhen(i -> Mono.just(i % 2 == 0), 3)
.doOnSubscribe(sub -> {
assertThat(sub).isInstanceOf(Scannable.class);
scannable.set((Scannable) sub);
});
StepVerifier.create(flux, 0)
.thenRequest(2)
.expectNext(2)
.then(() -> {
assertThat(scannable.get().scan(Scannable.Attr.PARENT)).isInstanceOf(FluxRange.RangeSubscription.class);
assertThat(scannable.get().scan(Scannable.Attr.ACTUAL)).isInstanceOf(FluxPeek.PeekSubscriber.class);
assertThat(scannable.get().scan(Scannable.Attr.PREFETCH)).isEqualTo(3);
assertThat(scannable.get().scan(Scannable.Attr.CAPACITY)).isEqualTo(4);
assertThat(scannable.get().scan(Scannable.Attr.ERROR)).isNull();
assertThat(scannable.get().scan(Scannable.Attr.BUFFERED )).isEqualTo(1L);
assertThat(scannable.get().scan(Scannable.Attr.REQUESTED_FROM_DOWNSTREAM)).isEqualTo(2L);
assertThat(scannable.get().scan(Scannable.Attr.CANCELLED)).isEqualTo(false);
assertThat(scannable.get().scan(Scannable.Attr.TERMINATED)).isEqualTo(false);
})
.thenRequest(2)
.expectNext(4)
.then(() -> {
assertThat(scannable.get().scan(Scannable.Attr.ERROR)).isNull();
assertThat(scannable.get().scan(Scannable.Attr.BUFFERED )).isEqualTo(2L);
assertThat(scannable.get().scan(Scannable.Attr.REQUESTED_FROM_DOWNSTREAM)).isEqualTo(4L);
assertThat(scannable.get().scan(Scannable.Attr.CANCELLED)).isEqualTo(false);
assertThat(scannable.get().scan(Scannable.Attr.TERMINATED)).isEqualTo(false);
})
.thenRequest(20)
.expectNext(6, 8, 10)
.verifyComplete();
assertThat(scannable.get().scan(Scannable.Attr.BUFFERED)).isEqualTo(6L);
assertThat(scannable.get().scan(Scannable.Attr.REQUESTED_FROM_DOWNSTREAM)).isEqualTo(24L);
assertThat(scannable.get().scan(Scannable.Attr.CANCELLED)).isEqualTo(false);
assertThat(scannable.get().scan(Scannable.Attr.TERMINATED)).isEqualTo(true);
}
//TODO introspect errors (but is difficult due to Expections.terminate)
@Test
public void introspectionCancel() {
AtomicReference<Scannable> scannable = new AtomicReference<>();
Flux<Integer> flux = Flux.range(1, 10).concatWith(Mono.error(new IllegalStateException("boom")))
.filterWhen(i -> Mono.just(i % 2 == 0), 3)
.doOnSubscribe(sub -> {
assertThat(sub).isInstanceOf(Scannable.class);
scannable.set((Scannable) sub);
});
StepVerifier.create(flux, 0)
.thenRequest(2)
.expectNext(2)
.then(() -> assertThat(scannable.get().scan(Scannable.Attr.CANCELLED)).isEqualTo(false))
.thenCancel()
.verify();
assertThat(scannable.get().scan(Scannable.Attr.CANCELLED)).isEqualTo(true);
}
}