/*
* 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.Subscriber;
import org.reactivestreams.Subscription;
import reactor.core.Scannable;
import reactor.test.StepVerifier;
import reactor.test.publisher.TestPublisher;
import static org.assertj.core.api.Assertions.assertThat;
public class MonoFilterWhenTest {
@Test
public void normalFiltered() {
StepVerifier.withVirtualTime(() -> Mono.just(1)
.filterWhen(v -> Mono.just(v % 2 == 0)
.delayElement(Duration.ofMillis(100))))
.expectSubscription()
.expectNoEvent(Duration.ofMillis(100))
.verifyComplete();
}
@Test
public void normalNotFiltered() {
StepVerifier.withVirtualTime(() -> Mono.just(2)
.filterWhen(v -> Mono.just(v % 2 == 0)
.delayElement(Duration.ofMillis(100))))
.expectSubscription()
.expectNoEvent(Duration.ofMillis(100))
.expectNext(2)
.verifyComplete();
}
@Test
public void normalSyncFiltered() {
StepVerifier.create(Mono.just(1)
.filterWhen(v -> Mono.just(v % 2 == 0).hide()))
.verifyComplete();
}
@Test
public void normalSyncNotFiltered() {
StepVerifier.create(Mono.just(2)
.filterWhen(v -> Mono.just(v % 2 == 0).hide()))
.expectNext(2)
.verifyComplete();
}
@Test
public void normalSyncFusedFiltered() {
StepVerifier.create(Mono.just(1)
.filterWhen(v -> Mono.just(v % 2 == 0)))
.verifyComplete();
}
@Test
public void normalSyncFusedNotFiltered() {
StepVerifier.create(Mono.just(2)
.filterWhen(v -> Mono.just(v % 2 == 0)))
.expectNext(2)
.verifyComplete();
}
@Test
public void allEmpty() {
StepVerifier.create(Mono.just(2)
.filterWhen(v -> Mono.<Boolean>empty().hide()))
.verifyComplete();
}
@Test
public void allEmptyFused() {
StepVerifier.create(Mono.just(2)
.filterWhen(v -> Mono.empty()))
.verifyComplete();
}
@Test
public void empty() {
StepVerifier.create(Mono.<Integer>empty()
.filterWhen(v -> Mono.just(true)))
.verifyComplete();
}
@Test
public void emptyBackpressured() {
StepVerifier.create(Mono.<Integer>empty()
.filterWhen(v -> Mono.just(true)), 0L)
.verifyComplete();
}
@Test
public void error() {
StepVerifier.create(Mono.<Integer>error(new IllegalStateException())
.filterWhen(v -> Mono.just(true)))
.verifyError(IllegalStateException.class);
}
@Test
public void errorBackpressured() {
StepVerifier.create(Mono.<Integer>error(new IllegalStateException())
.filterWhen(v -> Mono.just(true)), 0L)
.verifyError(IllegalStateException.class);
}
@Test
public void backpressureExactlyOne() {
StepVerifier.create(Mono.just(1)
.filterWhen(v -> Mono.just(true)), 1L)
.expectNext(1)
.verifyComplete();
}
@Test
public void oneAndErrorInner() {
StepVerifier.create(Mono.just(1)
.filterWhen(v -> s -> {
s.onSubscribe(Operators.emptySubscription());
s.onNext(true);
s.onError(new IllegalStateException());
}))
.expectNext(1)
.expectComplete()
.verifyThenAssertThat()
.hasDroppedErrorsSatisfying(
c -> assertThat(c)
.hasSize(1)
.element(0).isInstanceOf(IllegalStateException.class)
);
}
@Test
public void predicateThrows() {
StepVerifier.create(Mono.just(1)
.filterWhen(v -> { throw new IllegalStateException(); }))
.expectError(IllegalStateException.class);
}
@Test
public void predicateNull() {
StepVerifier.create(Mono.just(1).filterWhen(v -> null))
.verifyError(NullPointerException.class);
}
@Test
public void predicateError() {
StepVerifier.create(Mono.just(1)
.filterWhen(v -> Mono.<Boolean>error(new IllegalStateException()).hide()))
.verifyError(IllegalStateException.class);
}
@Test
public void predicateErrorFused() {
StepVerifier.create(Mono.just(1)
.filterWhen(v -> Mono.fromCallable(() -> { throw new IllegalStateException(); })))
.verifyError(IllegalStateException.class);
}
@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);
}
};
Mono.just(1)
.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);
}
};
Mono.just(1)
.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(Mono.just(1)
.filterWhen(v -> pp))
.thenCancel();
assertThat(pp.hasDownstreams()).isFalse();
}
@Test
public void innerFluxCancelled() {
AtomicInteger cancelCount = new AtomicInteger();
StepVerifier.create(Mono.just(1)
.filterWhen(v -> Flux.just(true, false, false)
.doOnCancel(cancelCount::incrementAndGet)))
.expectNext(1)
.verifyComplete();
assertThat(cancelCount.get()).isEqualTo(1);
}
@Test
public void innerFluxOnlyConsidersFirstValue() {
StepVerifier.create(Mono.just(1)
.filterWhen(v -> Flux.just(false, true, true)))
.verifyComplete();
}
@Test
public void innerMonoNotCancelled() {
AtomicInteger cancelCount = new AtomicInteger();
StepVerifier.create(Mono.just(3)
.filterWhen(v -> Mono.just(true)
.doOnCancel(cancelCount::incrementAndGet)))
.expectNext(3)
.verifyComplete();
assertThat(cancelCount.get()).isEqualTo(0);
}
@Test
public void scanTerminatedOnlyTrueIfFilterTerminated() {
AtomicReference<Subscriber> subscriber = new AtomicReference<>();
TestPublisher<Boolean> filter = TestPublisher.create();
new MonoFilterWhen(new Mono() {
@Override
public void subscribe(Subscriber s) {
subscriber.set(s);
//NON-EMPTY SOURCE WILL TRIGGER FILTER SUBSCRIPTION
s.onNext(2);
s.onComplete();
}
}, w -> filter)
.subscribe();
assertThat(subscriber.get()).isNotNull()
.isInstanceOf(Scannable.class);
Boolean terminated = ((Scannable) subscriber.get()).scan(Scannable.Attr.TERMINATED, Boolean.class);
assertThat(terminated).isFalse();
filter.emit(Boolean.TRUE);
terminated = ((Scannable) subscriber.get()).scan(Scannable.Attr.TERMINATED, Boolean.class);
assertThat(terminated).isTrue();
}
}