/**
* Copyright (c) 2016-present, RxJava Contributors.
*
* 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 io.reactivex.internal.operators.flowable;
import static org.junit.Assert.*;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicBoolean;
import org.junit.*;
import org.mockito.InOrder;
import org.reactivestreams.*;
import io.reactivex.*;
import io.reactivex.exceptions.*;
import io.reactivex.functions.*;
import io.reactivex.internal.functions.Functions;
import io.reactivex.internal.subscriptions.BooleanSubscription;
import io.reactivex.internal.util.ExceptionHelper;
import io.reactivex.plugins.RxJavaPlugins;
import io.reactivex.processors.PublishProcessor;
import io.reactivex.schedulers.TestScheduler;
import io.reactivex.subscribers.*;
public class FlowableSwitchTest {
private TestScheduler scheduler;
private Scheduler.Worker innerScheduler;
private Subscriber<String> observer;
@Before
public void before() {
scheduler = new TestScheduler();
innerScheduler = scheduler.createWorker();
observer = TestHelper.mockSubscriber();
}
@Test
public void testSwitchWhenOuterCompleteBeforeInner() {
Flowable<Flowable<String>> source = Flowable.unsafeCreate(new Publisher<Flowable<String>>() {
@Override
public void subscribe(Subscriber<? super Flowable<String>> observer) {
observer.onSubscribe(new BooleanSubscription());
publishNext(observer, 50, Flowable.unsafeCreate(new Publisher<String>() {
@Override
public void subscribe(Subscriber<? super String> observer) {
observer.onSubscribe(new BooleanSubscription());
publishNext(observer, 70, "one");
publishNext(observer, 100, "two");
publishCompleted(observer, 200);
}
}));
publishCompleted(observer, 60);
}
});
Flowable<String> sampled = Flowable.switchOnNext(source);
sampled.subscribe(observer);
InOrder inOrder = inOrder(observer);
scheduler.advanceTimeTo(350, TimeUnit.MILLISECONDS);
inOrder.verify(observer, times(2)).onNext(anyString());
inOrder.verify(observer, times(1)).onComplete();
}
@Test
public void testSwitchWhenInnerCompleteBeforeOuter() {
Flowable<Flowable<String>> source = Flowable.unsafeCreate(new Publisher<Flowable<String>>() {
@Override
public void subscribe(Subscriber<? super Flowable<String>> observer) {
observer.onSubscribe(new BooleanSubscription());
publishNext(observer, 10, Flowable.unsafeCreate(new Publisher<String>() {
@Override
public void subscribe(Subscriber<? super String> observer) {
observer.onSubscribe(new BooleanSubscription());
publishNext(observer, 0, "one");
publishNext(observer, 10, "two");
publishCompleted(observer, 20);
}
}));
publishNext(observer, 100, Flowable.unsafeCreate(new Publisher<String>() {
@Override
public void subscribe(Subscriber<? super String> observer) {
observer.onSubscribe(new BooleanSubscription());
publishNext(observer, 0, "three");
publishNext(observer, 10, "four");
publishCompleted(observer, 20);
}
}));
publishCompleted(observer, 200);
}
});
Flowable<String> sampled = Flowable.switchOnNext(source);
sampled.subscribe(observer);
InOrder inOrder = inOrder(observer);
scheduler.advanceTimeTo(150, TimeUnit.MILLISECONDS);
inOrder.verify(observer, never()).onComplete();
inOrder.verify(observer, times(1)).onNext("one");
inOrder.verify(observer, times(1)).onNext("two");
inOrder.verify(observer, times(1)).onNext("three");
inOrder.verify(observer, times(1)).onNext("four");
scheduler.advanceTimeTo(250, TimeUnit.MILLISECONDS);
inOrder.verify(observer, never()).onNext(anyString());
inOrder.verify(observer, times(1)).onComplete();
}
@Test
public void testSwitchWithComplete() {
Flowable<Flowable<String>> source = Flowable.unsafeCreate(new Publisher<Flowable<String>>() {
@Override
public void subscribe(Subscriber<? super Flowable<String>> observer) {
observer.onSubscribe(new BooleanSubscription());
publishNext(observer, 50, Flowable.unsafeCreate(new Publisher<String>() {
@Override
public void subscribe(final Subscriber<? super String> observer) {
observer.onSubscribe(new BooleanSubscription());
publishNext(observer, 60, "one");
publishNext(observer, 100, "two");
}
}));
publishNext(observer, 200, Flowable.unsafeCreate(new Publisher<String>() {
@Override
public void subscribe(final Subscriber<? super String> observer) {
observer.onSubscribe(new BooleanSubscription());
publishNext(observer, 0, "three");
publishNext(observer, 100, "four");
}
}));
publishCompleted(observer, 250);
}
});
Flowable<String> sampled = Flowable.switchOnNext(source);
sampled.subscribe(observer);
InOrder inOrder = inOrder(observer);
scheduler.advanceTimeTo(90, TimeUnit.MILLISECONDS);
inOrder.verify(observer, never()).onNext(anyString());
verify(observer, never()).onComplete();
verify(observer, never()).onError(any(Throwable.class));
scheduler.advanceTimeTo(125, TimeUnit.MILLISECONDS);
inOrder.verify(observer, times(1)).onNext("one");
verify(observer, never()).onComplete();
verify(observer, never()).onError(any(Throwable.class));
scheduler.advanceTimeTo(175, TimeUnit.MILLISECONDS);
inOrder.verify(observer, times(1)).onNext("two");
verify(observer, never()).onComplete();
verify(observer, never()).onError(any(Throwable.class));
scheduler.advanceTimeTo(225, TimeUnit.MILLISECONDS);
inOrder.verify(observer, times(1)).onNext("three");
verify(observer, never()).onComplete();
verify(observer, never()).onError(any(Throwable.class));
scheduler.advanceTimeTo(350, TimeUnit.MILLISECONDS);
inOrder.verify(observer, times(1)).onNext("four");
verify(observer, never()).onComplete();
verify(observer, never()).onError(any(Throwable.class));
}
@Test
public void testSwitchWithError() {
Flowable<Flowable<String>> source = Flowable.unsafeCreate(new Publisher<Flowable<String>>() {
@Override
public void subscribe(Subscriber<? super Flowable<String>> observer) {
observer.onSubscribe(new BooleanSubscription());
publishNext(observer, 50, Flowable.unsafeCreate(new Publisher<String>() {
@Override
public void subscribe(final Subscriber<? super String> observer) {
observer.onSubscribe(new BooleanSubscription());
publishNext(observer, 50, "one");
publishNext(observer, 100, "two");
}
}));
publishNext(observer, 200, Flowable.unsafeCreate(new Publisher<String>() {
@Override
public void subscribe(Subscriber<? super String> observer) {
observer.onSubscribe(new BooleanSubscription());
publishNext(observer, 0, "three");
publishNext(observer, 100, "four");
}
}));
publishError(observer, 250, new TestException());
}
});
Flowable<String> sampled = Flowable.switchOnNext(source);
sampled.subscribe(observer);
InOrder inOrder = inOrder(observer);
scheduler.advanceTimeTo(90, TimeUnit.MILLISECONDS);
inOrder.verify(observer, never()).onNext(anyString());
verify(observer, never()).onComplete();
verify(observer, never()).onError(any(Throwable.class));
scheduler.advanceTimeTo(125, TimeUnit.MILLISECONDS);
inOrder.verify(observer, times(1)).onNext("one");
verify(observer, never()).onComplete();
verify(observer, never()).onError(any(Throwable.class));
scheduler.advanceTimeTo(175, TimeUnit.MILLISECONDS);
inOrder.verify(observer, times(1)).onNext("two");
verify(observer, never()).onComplete();
verify(observer, never()).onError(any(Throwable.class));
scheduler.advanceTimeTo(225, TimeUnit.MILLISECONDS);
inOrder.verify(observer, times(1)).onNext("three");
verify(observer, never()).onComplete();
verify(observer, never()).onError(any(Throwable.class));
scheduler.advanceTimeTo(350, TimeUnit.MILLISECONDS);
inOrder.verify(observer, never()).onNext(anyString());
verify(observer, never()).onComplete();
verify(observer, times(1)).onError(any(TestException.class));
}
@Test
public void testSwitchWithSubsequenceComplete() {
Flowable<Flowable<String>> source = Flowable.unsafeCreate(new Publisher<Flowable<String>>() {
@Override
public void subscribe(Subscriber<? super Flowable<String>> observer) {
observer.onSubscribe(new BooleanSubscription());
publishNext(observer, 50, Flowable.unsafeCreate(new Publisher<String>() {
@Override
public void subscribe(Subscriber<? super String> observer) {
observer.onSubscribe(new BooleanSubscription());
publishNext(observer, 50, "one");
publishNext(observer, 100, "two");
}
}));
publishNext(observer, 130, Flowable.unsafeCreate(new Publisher<String>() {
@Override
public void subscribe(Subscriber<? super String> observer) {
observer.onSubscribe(new BooleanSubscription());
publishCompleted(observer, 0);
}
}));
publishNext(observer, 150, Flowable.unsafeCreate(new Publisher<String>() {
@Override
public void subscribe(Subscriber<? super String> observer) {
observer.onSubscribe(new BooleanSubscription());
publishNext(observer, 50, "three");
}
}));
}
});
Flowable<String> sampled = Flowable.switchOnNext(source);
sampled.subscribe(observer);
InOrder inOrder = inOrder(observer);
scheduler.advanceTimeTo(90, TimeUnit.MILLISECONDS);
inOrder.verify(observer, never()).onNext(anyString());
verify(observer, never()).onComplete();
verify(observer, never()).onError(any(Throwable.class));
scheduler.advanceTimeTo(125, TimeUnit.MILLISECONDS);
inOrder.verify(observer, times(1)).onNext("one");
verify(observer, never()).onComplete();
verify(observer, never()).onError(any(Throwable.class));
scheduler.advanceTimeTo(250, TimeUnit.MILLISECONDS);
inOrder.verify(observer, times(1)).onNext("three");
verify(observer, never()).onComplete();
verify(observer, never()).onError(any(Throwable.class));
}
@Test
public void testSwitchWithSubsequenceError() {
Flowable<Flowable<String>> source = Flowable.unsafeCreate(new Publisher<Flowable<String>>() {
@Override
public void subscribe(Subscriber<? super Flowable<String>> observer) {
observer.onSubscribe(new BooleanSubscription());
publishNext(observer, 50, Flowable.unsafeCreate(new Publisher<String>() {
@Override
public void subscribe(Subscriber<? super String> observer) {
observer.onSubscribe(new BooleanSubscription());
publishNext(observer, 50, "one");
publishNext(observer, 100, "two");
}
}));
publishNext(observer, 130, Flowable.unsafeCreate(new Publisher<String>() {
@Override
public void subscribe(Subscriber<? super String> observer) {
observer.onSubscribe(new BooleanSubscription());
publishError(observer, 0, new TestException());
}
}));
publishNext(observer, 150, Flowable.unsafeCreate(new Publisher<String>() {
@Override
public void subscribe(Subscriber<? super String> observer) {
observer.onSubscribe(new BooleanSubscription());
publishNext(observer, 50, "three");
}
}));
}
});
Flowable<String> sampled = Flowable.switchOnNext(source);
sampled.subscribe(observer);
InOrder inOrder = inOrder(observer);
scheduler.advanceTimeTo(90, TimeUnit.MILLISECONDS);
inOrder.verify(observer, never()).onNext(anyString());
verify(observer, never()).onComplete();
verify(observer, never()).onError(any(Throwable.class));
scheduler.advanceTimeTo(125, TimeUnit.MILLISECONDS);
inOrder.verify(observer, times(1)).onNext("one");
verify(observer, never()).onComplete();
verify(observer, never()).onError(any(Throwable.class));
scheduler.advanceTimeTo(250, TimeUnit.MILLISECONDS);
inOrder.verify(observer, never()).onNext("three");
verify(observer, never()).onComplete();
verify(observer, times(1)).onError(any(TestException.class));
}
private <T> void publishCompleted(final Subscriber<T> observer, long delay) {
innerScheduler.schedule(new Runnable() {
@Override
public void run() {
observer.onComplete();
}
}, delay, TimeUnit.MILLISECONDS);
}
private <T> void publishError(final Subscriber<T> observer, long delay, final Throwable error) {
innerScheduler.schedule(new Runnable() {
@Override
public void run() {
observer.onError(error);
}
}, delay, TimeUnit.MILLISECONDS);
}
private <T> void publishNext(final Subscriber<T> observer, long delay, final T value) {
innerScheduler.schedule(new Runnable() {
@Override
public void run() {
observer.onNext(value);
}
}, delay, TimeUnit.MILLISECONDS);
}
@Test
public void testSwitchIssue737() {
// https://github.com/ReactiveX/RxJava/issues/737
Flowable<Flowable<String>> source = Flowable.unsafeCreate(new Publisher<Flowable<String>>() {
@Override
public void subscribe(Subscriber<? super Flowable<String>> observer) {
observer.onSubscribe(new BooleanSubscription());
publishNext(observer, 0, Flowable.unsafeCreate(new Publisher<String>() {
@Override
public void subscribe(Subscriber<? super String> observer) {
observer.onSubscribe(new BooleanSubscription());
publishNext(observer, 10, "1-one");
publishNext(observer, 20, "1-two");
// The following events will be ignored
publishNext(observer, 30, "1-three");
publishCompleted(observer, 40);
}
}));
publishNext(observer, 25, Flowable.unsafeCreate(new Publisher<String>() {
@Override
public void subscribe(Subscriber<? super String> observer) {
observer.onSubscribe(new BooleanSubscription());
publishNext(observer, 10, "2-one");
publishNext(observer, 20, "2-two");
publishNext(observer, 30, "2-three");
publishCompleted(observer, 40);
}
}));
publishCompleted(observer, 30);
}
});
Flowable<String> sampled = Flowable.switchOnNext(source);
sampled.subscribe(observer);
scheduler.advanceTimeTo(1000, TimeUnit.MILLISECONDS);
InOrder inOrder = inOrder(observer);
inOrder.verify(observer, times(1)).onNext("1-one");
inOrder.verify(observer, times(1)).onNext("1-two");
inOrder.verify(observer, times(1)).onNext("2-one");
inOrder.verify(observer, times(1)).onNext("2-two");
inOrder.verify(observer, times(1)).onNext("2-three");
inOrder.verify(observer, times(1)).onComplete();
inOrder.verifyNoMoreInteractions();
}
@Test
public void testBackpressure() {
PublishProcessor<String> o1 = PublishProcessor.create();
PublishProcessor<String> o2 = PublishProcessor.create();
PublishProcessor<String> o3 = PublishProcessor.create();
PublishProcessor<PublishProcessor<String>> o = PublishProcessor.create();
publishNext(o, 0, o1);
publishNext(o, 5, o2);
publishNext(o, 10, o3);
publishCompleted(o, 15);
for (int i = 0; i < 10; i++) {
publishNext(o1, i * 5, "a" + (i + 1));
publishNext(o2, 5 + i * 5, "b" + (i + 1));
publishNext(o3, 10 + i * 5, "c" + (i + 1));
}
publishCompleted(o1, 45);
publishCompleted(o2, 50);
publishCompleted(o3, 55);
final TestSubscriber<String> testSubscriber = new TestSubscriber<String>();
Flowable.switchOnNext(o).subscribe(new DefaultSubscriber<String>() {
private int requested;
@Override
public void onStart() {
requested = 3;
request(3);
testSubscriber.onSubscribe(new BooleanSubscription());
}
@Override
public void onComplete() {
testSubscriber.onComplete();
}
@Override
public void onError(Throwable e) {
testSubscriber.onError(e);
}
@Override
public void onNext(String s) {
testSubscriber.onNext(s);
requested--;
if (requested == 0) {
requested = 3;
request(3);
}
}
});
scheduler.advanceTimeBy(1, TimeUnit.SECONDS);
testSubscriber.assertValues("a1", "b1", "c1", "c2", "c3", "c4", "c5", "c6", "c7", "c8", "c9", "c10");
testSubscriber.assertNoErrors();
testSubscriber.assertTerminated();
}
@Test
public void testUnsubscribe() {
final AtomicBoolean isUnsubscribed = new AtomicBoolean();
Flowable.switchOnNext(
Flowable.unsafeCreate(new Publisher<Flowable<Integer>>() {
@Override
public void subscribe(final Subscriber<? super Flowable<Integer>> subscriber) {
BooleanSubscription bs = new BooleanSubscription();
subscriber.onSubscribe(bs);
subscriber.onNext(Flowable.just(1));
isUnsubscribed.set(bs.isCancelled());
}
})
).take(1).subscribe();
assertTrue("Switch doesn't propagate 'unsubscribe'", isUnsubscribed.get());
}
/** The upstream producer hijacked the switch producer stopping the requests aimed at the inner observables. */
@Test
public void testIssue2654() {
Flowable<String> oneItem = Flowable.just("Hello").mergeWith(Flowable.<String>never());
Flowable<String> src = oneItem.switchMap(new Function<String, Flowable<String>>() {
@Override
public Flowable<String> apply(final String s) {
return Flowable.just(s)
.mergeWith(Flowable.interval(10, TimeUnit.MILLISECONDS)
.map(new Function<Long, String>() {
@Override
public String apply(Long i) {
return s + " " + i;
}
})).take(250);
}
})
.share()
;
TestSubscriber<String> ts = new TestSubscriber<String>() {
@Override
public void onNext(String t) {
super.onNext(t);
if (valueCount() == 250) {
onComplete();
dispose();
}
}
};
src.subscribe(ts);
ts.awaitTerminalEvent(10, TimeUnit.SECONDS);
System.out.println("> testIssue2654: " + ts.valueCount());
ts.assertTerminated();
ts.assertNoErrors();
Assert.assertEquals(250, ts.valueCount());
}
@Test(timeout = 10000)
public void testInitialRequestsAreAdditive() {
TestSubscriber<Long> ts = new TestSubscriber<Long>(0L);
Flowable.switchOnNext(
Flowable.interval(100, TimeUnit.MILLISECONDS)
.map(
new Function<Long, Flowable<Long>>() {
@Override
public Flowable<Long> apply(Long t) {
return Flowable.just(1L, 2L, 3L);
}
}
).take(3))
.subscribe(ts);
ts.request(Long.MAX_VALUE - 100);
ts.request(1);
ts.awaitTerminalEvent();
}
@Test(timeout = 10000)
public void testInitialRequestsDontOverflow() {
TestSubscriber<Long> ts = new TestSubscriber<Long>(0L);
Flowable.switchOnNext(
Flowable.interval(100, TimeUnit.MILLISECONDS)
.map(new Function<Long, Flowable<Long>>() {
@Override
public Flowable<Long> apply(Long t) {
return Flowable.fromIterable(Arrays.asList(1L, 2L, 3L)).hide();
}
}).take(3)).subscribe(ts);
ts.request(Long.MAX_VALUE - 1);
ts.request(2);
ts.awaitTerminalEvent();
assertTrue(ts.valueCount() > 0);
}
@Test(timeout = 10000)
public void testSecondaryRequestsDontOverflow() throws InterruptedException {
TestSubscriber<Long> ts = new TestSubscriber<Long>(0L);
Flowable.switchOnNext(
Flowable.interval(100, TimeUnit.MILLISECONDS)
.map(new Function<Long, Flowable<Long>>() {
@Override
public Flowable<Long> apply(Long t) {
return Flowable.fromIterable(Arrays.asList(1L, 2L, 3L)).hide();
}
}).take(3)).subscribe(ts);
ts.request(1);
//we will miss two of the first observable
Thread.sleep(250);
ts.request(Long.MAX_VALUE - 1);
ts.request(Long.MAX_VALUE - 1);
ts.awaitTerminalEvent();
ts.assertValueCount(7);
}
@Test(timeout = 10000)
@Ignore("Request pattern changed and I can't decide if this is okay or not")
public void testSecondaryRequestsAdditivelyAreMoreThanLongMaxValueInducesMaxValueRequestFromUpstream()
throws InterruptedException {
final List<Long> requests = new CopyOnWriteArrayList<Long>();
TestSubscriber<Long> ts = new TestSubscriber<Long>(1L);
Flowable.switchOnNext(
Flowable.interval(100, TimeUnit.MILLISECONDS)
.map(new Function<Long, Flowable<Long>>() {
@Override
public Flowable<Long> apply(Long t) {
return Flowable.fromIterable(Arrays.asList(1L, 2L, 3L))
.doOnRequest(new LongConsumer() {
@Override
public void accept(long v) {
requests.add(v);
}
});
}
}).take(3)).subscribe(ts);
// we will miss two of the first observables
Thread.sleep(250);
ts.request(Long.MAX_VALUE - 1);
ts.request(Long.MAX_VALUE - 1);
ts.awaitTerminalEvent();
assertTrue(ts.valueCount() > 0);
System.out.println(requests);
assertEquals(5, requests.size());
assertEquals(Long.MAX_VALUE, (long) requests.get(requests.size() - 1));
}
@Test
public void delayErrors() {
PublishProcessor<Publisher<Integer>> source = PublishProcessor.create();
TestSubscriber<Integer> ts = source.switchMapDelayError(Functions.<Publisher<Integer>>identity())
.test();
ts.assertNoValues()
.assertNoErrors()
.assertNotComplete();
source.onNext(Flowable.just(1));
source.onNext(Flowable.<Integer>error(new TestException("Forced failure 1")));
source.onNext(Flowable.just(2, 3, 4));
source.onNext(Flowable.<Integer>error(new TestException("Forced failure 2")));
source.onNext(Flowable.just(5));
source.onError(new TestException("Forced failure 3"));
ts.assertValues(1, 2, 3, 4, 5)
.assertNotComplete()
.assertError(CompositeException.class);
List<Throwable> errors = ExceptionHelper.flatten(ts.errors().get(0));
TestHelper.assertError(errors, 0, TestException.class, "Forced failure 1");
TestHelper.assertError(errors, 1, TestException.class, "Forced failure 2");
TestHelper.assertError(errors, 2, TestException.class, "Forced failure 3");
}
@Test
public void switchOnNextPrefetch() {
final List<Integer> list = new ArrayList<Integer>();
Flowable<Integer> source = Flowable.range(1, 10).hide().doOnNext(new Consumer<Integer>() {
@Override
public void accept(Integer v) throws Exception {
list.add(v);
}
});
Flowable.switchOnNext(Flowable.just(source).hide(), 2)
.test(1);
assertEquals(Arrays.asList(1, 2, 3), list);
}
@Test
public void switchOnNextDelayError() {
final List<Integer> list = new ArrayList<Integer>();
Flowable<Integer> source = Flowable.range(1, 10).hide().doOnNext(new Consumer<Integer>() {
@Override
public void accept(Integer v) throws Exception {
list.add(v);
}
});
Flowable.switchOnNextDelayError(Flowable.just(source).hide())
.test(1);
assertEquals(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10), list);
}
@Test
public void switchOnNextDelayErrorPrefetch() {
final List<Integer> list = new ArrayList<Integer>();
Flowable<Integer> source = Flowable.range(1, 10).hide().doOnNext(new Consumer<Integer>() {
@Override
public void accept(Integer v) throws Exception {
list.add(v);
}
});
Flowable.switchOnNextDelayError(Flowable.just(source).hide(), 2)
.test(1);
assertEquals(Arrays.asList(1, 2, 3), list);
}
@Test
public void switchOnNextDelayErrorWithError() {
PublishProcessor<Flowable<Integer>> ps = PublishProcessor.create();
TestSubscriber<Integer> ts = Flowable.switchOnNextDelayError(ps).test();
ps.onNext(Flowable.just(1));
ps.onNext(Flowable.<Integer>error(new TestException()));
ps.onNext(Flowable.range(2, 4));
ps.onComplete();
ts.assertFailure(TestException.class, 1, 2, 3, 4, 5);
}
@Test
public void switchOnNextDelayErrorBufferSize() {
PublishProcessor<Flowable<Integer>> ps = PublishProcessor.create();
TestSubscriber<Integer> ts = Flowable.switchOnNextDelayError(ps, 2).test();
ps.onNext(Flowable.just(1));
ps.onNext(Flowable.range(2, 4));
ps.onComplete();
ts.assertResult(1, 2, 3, 4, 5);
}
@Test
public void switchMapDelayErrorEmptySource() {
assertSame(Flowable.empty(), Flowable.<Object>empty()
.switchMapDelayError(new Function<Object, Publisher<Integer>>() {
@Override
public Publisher<Integer> apply(Object v) throws Exception {
return Flowable.just(1);
}
}, 16));
}
@Test
public void switchMapDelayErrorJustSource() {
Flowable.just(0)
.switchMapDelayError(new Function<Object, Publisher<Integer>>() {
@Override
public Publisher<Integer> apply(Object v) throws Exception {
return Flowable.just(1);
}
}, 16)
.test()
.assertResult(1);
}
@Test
public void switchMapErrorEmptySource() {
assertSame(Flowable.empty(), Flowable.<Object>empty()
.switchMap(new Function<Object, Publisher<Integer>>() {
@Override
public Publisher<Integer> apply(Object v) throws Exception {
return Flowable.just(1);
}
}, 16));
}
@Test
public void switchMapJustSource() {
Flowable.just(0)
.switchMap(new Function<Object, Publisher<Integer>>() {
@Override
public Publisher<Integer> apply(Object v) throws Exception {
return Flowable.just(1);
}
}, 16)
.test()
.assertResult(1);
}
@Test
public void switchMapInnerCancelled() {
PublishProcessor<Integer> pp = PublishProcessor.create();
TestSubscriber<Integer> ts = Flowable.just(1)
.switchMap(Functions.justFunction(pp))
.test();
assertTrue(pp.hasSubscribers());
ts.cancel();
assertFalse(pp.hasSubscribers());
}
@Test
public void dispose() {
TestHelper.checkDisposed(Flowable.switchOnNext(
Flowable.just(Flowable.just(1)).hide()));
}
@Test
public void nextSourceErrorRace() {
for (int i = 0; i < 500; i++) {
List<Throwable> errors = TestHelper.trackPluginErrors();
try {
final PublishProcessor<Integer> ps1 = PublishProcessor.create();
final PublishProcessor<Integer> ps2 = PublishProcessor.create();
ps1.switchMap(new Function<Integer, Flowable<Integer>>() {
@Override
public Flowable<Integer> apply(Integer v) throws Exception {
if (v == 1) {
return ps2;
}
return Flowable.never();
}
})
.test();
Runnable r1 = new Runnable() {
@Override
public void run() {
ps1.onNext(2);
}
};
final TestException ex = new TestException();
Runnable r2 = new Runnable() {
@Override
public void run() {
ps2.onError(ex);
}
};
TestHelper.race(r1, r2);
for (Throwable e : errors) {
assertTrue(e.toString(), e instanceof TestException);
}
} finally {
RxJavaPlugins.reset();
}
}
}
@Test
public void outerInnerErrorRace() {
for (int i = 0; i < 500; i++) {
List<Throwable> errors = TestHelper.trackPluginErrors();
try {
final PublishProcessor<Integer> ps1 = PublishProcessor.create();
final PublishProcessor<Integer> ps2 = PublishProcessor.create();
ps1.switchMap(new Function<Integer, Flowable<Integer>>() {
@Override
public Flowable<Integer> apply(Integer v) throws Exception {
if (v == 1) {
return ps2;
}
return Flowable.never();
}
})
.test();
final TestException ex1 = new TestException();
Runnable r1 = new Runnable() {
@Override
public void run() {
ps1.onError(ex1);
}
};
final TestException ex2 = new TestException();
Runnable r2 = new Runnable() {
@Override
public void run() {
ps2.onError(ex2);
}
};
TestHelper.race(r1, r2);
for (Throwable e : errors) {
assertTrue(e.toString(), e instanceof TestException);
}
} finally {
RxJavaPlugins.reset();
}
}
}
@Test
public void nextCancelRace() {
for (int i = 0; i < 500; i++) {
final PublishProcessor<Integer> ps1 = PublishProcessor.create();
final TestSubscriber<Integer> to = ps1.switchMap(new Function<Integer, Flowable<Integer>>() {
@Override
public Flowable<Integer> apply(Integer v) throws Exception {
return Flowable.never();
}
})
.test();
Runnable r1 = new Runnable() {
@Override
public void run() {
ps1.onNext(2);
}
};
Runnable r2 = new Runnable() {
@Override
public void run() {
to.cancel();
}
};
TestHelper.race(r1, r2);
}
}
@Test
public void mapperThrows() {
Flowable.just(1).hide()
.switchMap(new Function<Integer, Flowable<Object>>() {
@Override
public Flowable<Object> apply(Integer v) throws Exception {
throw new TestException();
}
})
.test()
.assertFailure(TestException.class);
}
@Test
public void badMainSource() {
List<Throwable> errors = TestHelper.trackPluginErrors();
try {
new Flowable<Integer>() {
@Override
protected void subscribeActual(Subscriber<? super Integer> observer) {
observer.onSubscribe(new BooleanSubscription());
observer.onComplete();
observer.onError(new TestException());
observer.onComplete();
}
}
.switchMap(Functions.justFunction(Flowable.never()))
.test()
.assertResult();
TestHelper.assertUndeliverable(errors, 0, TestException.class);
} finally {
RxJavaPlugins.reset();
}
}
@Test
public void emptyInner() {
Flowable.range(1, 5)
.switchMap(Functions.justFunction(Flowable.empty()))
.test()
.assertResult();
}
@Test
public void justInner() {
Flowable.range(1, 5)
.switchMap(Functions.justFunction(Flowable.just(1)))
.test()
.assertResult(1, 1, 1, 1, 1);
}
@Test
public void badInnerSource() {
List<Throwable> errors = TestHelper.trackPluginErrors();
try {
Flowable.just(1).hide()
.switchMap(Functions.justFunction(new Flowable<Integer>() {
@Override
protected void subscribeActual(Subscriber<? super Integer> observer) {
observer.onSubscribe(new BooleanSubscription());
observer.onError(new TestException());
observer.onComplete();
observer.onError(new TestException());
observer.onComplete();
}
}))
.test()
.assertFailure(TestException.class);
TestHelper.assertUndeliverable(errors, 0, TestException.class);
} finally {
RxJavaPlugins.reset();
}
}
@Test
public void innerCompletesReentrant() {
final PublishProcessor<Integer> ps = PublishProcessor.create();
TestSubscriber<Integer> to = new TestSubscriber<Integer>() {
@Override
public void onNext(Integer t) {
super.onNext(t);
ps.onComplete();
}
};
Flowable.just(1).hide()
.switchMap(Functions.justFunction(ps))
.subscribe(to);
ps.onNext(1);
to.assertResult(1);
}
@Test
public void innerErrorsReentrant() {
final PublishProcessor<Integer> ps = PublishProcessor.create();
TestSubscriber<Integer> to = new TestSubscriber<Integer>() {
@Override
public void onNext(Integer t) {
super.onNext(t);
ps.onError(new TestException());
}
};
Flowable.just(1).hide()
.switchMap(Functions.justFunction(ps))
.subscribe(to);
ps.onNext(1);
to.assertFailure(TestException.class, 1);
}
@Test
public void scalarMap() {
Flowable.switchOnNext(Flowable.just(Flowable.just(1)))
.test()
.assertResult(1);
}
@Test
public void scalarMapDelayError() {
Flowable.switchOnNextDelayError(Flowable.just(Flowable.just(1)))
.test()
.assertResult(1);
}
@Test
public void scalarXMap() {
Flowable.fromCallable(Functions.justCallable(1))
.switchMap(Functions.justFunction(Flowable.just(1)))
.test()
.assertResult(1);
}
@Test
public void badSource() {
TestHelper.checkBadSourceFlowable(new Function<Flowable<Integer>, Object>() {
@Override
public Object apply(Flowable<Integer> f) throws Exception {
return f.switchMap(Functions.justFunction(Flowable.just(1)));
}
}, false, 1, 1, 1);
}
@Test
public void innerOverflow() {
Flowable.just(1).hide()
.switchMap(Functions.justFunction(new Flowable<Integer>() {
@Override
protected void subscribeActual(Subscriber<? super Integer> s) {
s.onSubscribe(new BooleanSubscription());
for (int i = 0; i < 10; i++) {
s.onNext(i);
}
}
}), 8)
.test(1L)
.assertFailure(MissingBackpressureException.class, 0);
}
@Test
public void drainCancelRace() {
for (int i = 0; i < 500; i++) {
final TestSubscriber<Integer> ts = new TestSubscriber<Integer>();
final PublishProcessor<Integer> pp = PublishProcessor.create();
Flowable.just(1).hide()
.switchMap(Functions.justFunction(pp))
.subscribe(ts);
Runnable r1 = new Runnable() {
@Override
public void run() {
ts.cancel();
}
};
Runnable r2 = new Runnable() {
@Override
public void run() {
pp.onNext(1);
}
};
TestHelper.race(r1, r2);
}
}
@Test
public void fusedInnerCrash() {
Flowable.just(1).hide()
.switchMap(Functions.justFunction(Flowable.just(1).map(new Function<Integer, Object>() {
@Override
public Object apply(Integer v) throws Exception {
throw new TestException();
}
})))
.test()
.assertFailure(TestException.class);
}
}