/** * 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 java.lang.reflect.Method; import java.util.*; import java.util.concurrent.*; import java.util.concurrent.atomic.*; import org.junit.*; 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.plugins.RxJavaPlugins; import io.reactivex.processors.*; import io.reactivex.schedulers.Schedulers; import io.reactivex.subscribers.TestSubscriber; public class FlowableConcatMapEagerTest { @Test public void normal() { Flowable.range(1, 5) .concatMapEager(new Function<Integer, Publisher<Integer>>() { @Override public Publisher<Integer> apply(Integer t) { return Flowable.range(t, 2); } }) .test() .assertResult(1, 2, 2, 3, 3, 4, 4, 5, 5, 6); } @Test public void normalBackpressured() { TestSubscriber<Integer> ts = Flowable.range(1, 5) .concatMapEager(new Function<Integer, Publisher<Integer>>() { @Override public Publisher<Integer> apply(Integer t) { return Flowable.range(t, 2); } }) .test(3); ts.assertValues(1, 2, 2); ts.request(1); ts.assertValues(1, 2, 2, 3); ts.request(1); ts.assertValues(1, 2, 2, 3, 3); ts.request(5); ts.assertResult(1, 2, 2, 3, 3, 4, 4, 5, 5, 6); } @Test public void normalDelayBoundary() { Flowable.range(1, 5) .concatMapEagerDelayError(new Function<Integer, Publisher<Integer>>() { @Override public Publisher<Integer> apply(Integer t) { return Flowable.range(t, 2); } }, false) .test() .assertResult(1, 2, 2, 3, 3, 4, 4, 5, 5, 6); } @Test public void normalDelayBoundaryBackpressured() { TestSubscriber<Integer> ts = Flowable.range(1, 5) .concatMapEagerDelayError(new Function<Integer, Publisher<Integer>>() { @Override public Publisher<Integer> apply(Integer t) { return Flowable.range(t, 2); } }, false) .test(3); ts.assertValues(1, 2, 2); ts.request(1); ts.assertValues(1, 2, 2, 3); ts.request(1); ts.assertValues(1, 2, 2, 3, 3); ts.request(5); ts.assertResult(1, 2, 2, 3, 3, 4, 4, 5, 5, 6); } @Test public void normalDelayEnd() { Flowable.range(1, 5) .concatMapEagerDelayError(new Function<Integer, Publisher<Integer>>() { @Override public Publisher<Integer> apply(Integer t) { return Flowable.range(t, 2); } }, true) .test() .assertResult(1, 2, 2, 3, 3, 4, 4, 5, 5, 6); } @Test public void normalDelayEndBackpressured() { TestSubscriber<Integer> ts = Flowable.range(1, 5) .concatMapEagerDelayError(new Function<Integer, Publisher<Integer>>() { @Override public Publisher<Integer> apply(Integer t) { return Flowable.range(t, 2); } }, true) .test(3); ts.assertValues(1, 2, 2); ts.request(1); ts.assertValues(1, 2, 2, 3); ts.request(1); ts.assertValues(1, 2, 2, 3, 3); ts.request(5); ts.assertResult(1, 2, 2, 3, 3, 4, 4, 5, 5, 6); } @Test public void mainErrorsDelayBoundary() { PublishProcessor<Integer> main = PublishProcessor.create(); final PublishProcessor<Integer> inner = PublishProcessor.create(); TestSubscriber<Integer> ts = main.concatMapEagerDelayError( new Function<Integer, Publisher<Integer>>() { @Override public Publisher<Integer> apply(Integer t) { return inner; } }, false).test(); main.onNext(1); inner.onNext(2); ts.assertValue(2); main.onError(new TestException("Forced failure")); ts.assertNoErrors(); inner.onNext(3); inner.onComplete(); ts.assertFailureAndMessage(TestException.class, "Forced failure", 2, 3); } @Test public void mainErrorsDelayEnd() { PublishProcessor<Integer> main = PublishProcessor.create(); final PublishProcessor<Integer> inner = PublishProcessor.create(); TestSubscriber<Integer> ts = main.concatMapEagerDelayError( new Function<Integer, Publisher<Integer>>() { @Override public Publisher<Integer> apply(Integer t) { return inner; } }, true).test(); main.onNext(1); main.onNext(2); inner.onNext(2); ts.assertValue(2); main.onError(new TestException("Forced failure")); ts.assertNoErrors(); inner.onNext(3); inner.onComplete(); ts.assertFailureAndMessage(TestException.class, "Forced failure", 2, 3, 2, 3); } @Test public void mainErrorsImmediate() { PublishProcessor<Integer> main = PublishProcessor.create(); final PublishProcessor<Integer> inner = PublishProcessor.create(); TestSubscriber<Integer> ts = main.concatMapEager( new Function<Integer, Publisher<Integer>>() { @Override public Publisher<Integer> apply(Integer t) { return inner; } }).test(); main.onNext(1); main.onNext(2); inner.onNext(2); ts.assertValue(2); main.onError(new TestException("Forced failure")); assertFalse("inner has subscribers?", inner.hasSubscribers()); inner.onNext(3); inner.onComplete(); ts.assertFailureAndMessage(TestException.class, "Forced failure", 2); } @Test public void longEager() { Flowable.range(1, 2 * Flowable.bufferSize()) .concatMapEager(new Function<Integer, Publisher<Integer>>() { @Override public Publisher<Integer> apply(Integer v) { return Flowable.just(1); } }) .test() .assertValueCount(2 * Flowable.bufferSize()) .assertNoErrors() .assertComplete(); } TestSubscriber<Object> ts; TestSubscriber<Object> tsBp; Function<Integer, Flowable<Integer>> toJust = new Function<Integer, Flowable<Integer>>() { @Override public Flowable<Integer> apply(Integer t) { return Flowable.just(t); } }; Function<Integer, Flowable<Integer>> toRange = new Function<Integer, Flowable<Integer>>() { @Override public Flowable<Integer> apply(Integer t) { return Flowable.range(t, 2); } }; @Before public void before() { ts = new TestSubscriber<Object>(); tsBp = new TestSubscriber<Object>(0L); } @Test public void testSimple() { Flowable.range(1, 100).concatMapEager(toJust).subscribe(ts); ts.assertNoErrors(); ts.assertValueCount(100); ts.assertComplete(); } @Test public void testSimple2() { Flowable.range(1, 100).concatMapEager(toRange).subscribe(ts); ts.assertNoErrors(); ts.assertValueCount(200); ts.assertComplete(); } @SuppressWarnings("unchecked") @Test public void testEagerness2() { final AtomicInteger count = new AtomicInteger(); Flowable<Integer> source = Flowable.just(1).doOnNext(new Consumer<Integer>() { @Override public void accept(Integer t) { count.getAndIncrement(); } }).hide(); Flowable.concatArrayEager(source, source).subscribe(tsBp); Assert.assertEquals(2, count.get()); tsBp.assertNoErrors(); tsBp.assertNotComplete(); tsBp.assertNoValues(); tsBp.request(Long.MAX_VALUE); tsBp.assertValueCount(count.get()); tsBp.assertNoErrors(); tsBp.assertComplete(); } @SuppressWarnings("unchecked") @Test public void testEagerness3() { final AtomicInteger count = new AtomicInteger(); Flowable<Integer> source = Flowable.just(1).doOnNext(new Consumer<Integer>() { @Override public void accept(Integer t) { count.getAndIncrement(); } }).hide(); Flowable.concatArrayEager(source, source, source).subscribe(tsBp); Assert.assertEquals(3, count.get()); tsBp.assertNoErrors(); tsBp.assertNotComplete(); tsBp.assertNoValues(); tsBp.request(Long.MAX_VALUE); tsBp.assertValueCount(count.get()); tsBp.assertNoErrors(); tsBp.assertComplete(); } @SuppressWarnings("unchecked") @Test public void testEagerness4() { final AtomicInteger count = new AtomicInteger(); Flowable<Integer> source = Flowable.just(1).doOnNext(new Consumer<Integer>() { @Override public void accept(Integer t) { count.getAndIncrement(); } }).hide(); Flowable.concatArrayEager(source, source, source, source).subscribe(tsBp); Assert.assertEquals(4, count.get()); tsBp.assertNoErrors(); tsBp.assertNotComplete(); tsBp.assertNoValues(); tsBp.request(Long.MAX_VALUE); tsBp.assertValueCount(count.get()); tsBp.assertNoErrors(); tsBp.assertComplete(); } @SuppressWarnings("unchecked") @Test public void testEagerness5() { final AtomicInteger count = new AtomicInteger(); Flowable<Integer> source = Flowable.just(1).doOnNext(new Consumer<Integer>() { @Override public void accept(Integer t) { count.getAndIncrement(); } }).hide(); Flowable.concatArrayEager(source, source, source, source, source).subscribe(tsBp); Assert.assertEquals(5, count.get()); tsBp.assertNoErrors(); tsBp.assertNotComplete(); tsBp.assertNoValues(); tsBp.request(Long.MAX_VALUE); tsBp.assertValueCount(count.get()); tsBp.assertNoErrors(); tsBp.assertComplete(); } @SuppressWarnings("unchecked") @Test public void testEagerness6() { final AtomicInteger count = new AtomicInteger(); Flowable<Integer> source = Flowable.just(1).doOnNext(new Consumer<Integer>() { @Override public void accept(Integer t) { count.getAndIncrement(); } }).hide(); Flowable.concatArrayEager(source, source, source, source, source, source).subscribe(tsBp); Assert.assertEquals(6, count.get()); tsBp.assertNoErrors(); tsBp.assertNotComplete(); tsBp.assertNoValues(); tsBp.request(Long.MAX_VALUE); tsBp.assertValueCount(count.get()); tsBp.assertNoErrors(); tsBp.assertComplete(); } @SuppressWarnings("unchecked") @Test public void testEagerness7() { final AtomicInteger count = new AtomicInteger(); Flowable<Integer> source = Flowable.just(1).doOnNext(new Consumer<Integer>() { @Override public void accept(Integer t) { count.getAndIncrement(); } }).hide(); Flowable.concatArrayEager(source, source, source, source, source, source, source).subscribe(tsBp); Assert.assertEquals(7, count.get()); tsBp.assertNoErrors(); tsBp.assertNotComplete(); tsBp.assertNoValues(); tsBp.request(Long.MAX_VALUE); tsBp.assertValueCount(count.get()); tsBp.assertNoErrors(); tsBp.assertComplete(); } @SuppressWarnings("unchecked") @Test public void testEagerness8() { final AtomicInteger count = new AtomicInteger(); Flowable<Integer> source = Flowable.just(1).doOnNext(new Consumer<Integer>() { @Override public void accept(Integer t) { count.getAndIncrement(); } }).hide(); Flowable.concatArrayEager(source, source, source, source, source, source, source, source).subscribe(tsBp); Assert.assertEquals(8, count.get()); tsBp.assertNoErrors(); tsBp.assertNotComplete(); tsBp.assertNoValues(); tsBp.request(Long.MAX_VALUE); tsBp.assertValueCount(count.get()); tsBp.assertNoErrors(); tsBp.assertComplete(); } @SuppressWarnings("unchecked") @Test public void testEagerness9() { final AtomicInteger count = new AtomicInteger(); Flowable<Integer> source = Flowable.just(1).doOnNext(new Consumer<Integer>() { @Override public void accept(Integer t) { count.getAndIncrement(); } }).hide(); Flowable.concatArrayEager(source, source, source, source, source, source, source, source, source).subscribe(tsBp); Assert.assertEquals(9, count.get()); tsBp.assertNoErrors(); tsBp.assertNotComplete(); tsBp.assertNoValues(); tsBp.request(Long.MAX_VALUE); tsBp.assertValueCount(count.get()); tsBp.assertNoErrors(); tsBp.assertComplete(); } @Test public void testMainError() { Flowable.<Integer>error(new TestException()).concatMapEager(toJust).subscribe(ts); ts.assertNoValues(); ts.assertError(TestException.class); ts.assertNotComplete(); } @SuppressWarnings("unchecked") @Test public void testInnerError() { Flowable.concatArrayEager(Flowable.just(1), Flowable.error(new TestException())).subscribe(ts); ts.assertValue(1); ts.assertError(TestException.class); ts.assertNotComplete(); } @SuppressWarnings("unchecked") @Test public void testInnerEmpty() { Flowable.concatArrayEager(Flowable.empty(), Flowable.empty()).subscribe(ts); ts.assertNoValues(); ts.assertNoErrors(); ts.assertComplete(); } @Test public void testMapperThrows() { Flowable.just(1).concatMapEager(new Function<Integer, Flowable<Integer>>() { @Override public Flowable<Integer> apply(Integer t) { throw new TestException(); } }).subscribe(ts); ts.assertNoValues(); ts.assertNotComplete(); ts.assertError(TestException.class); } @Test(expected = IllegalArgumentException.class) public void testInvalidMaxConcurrent() { Flowable.just(1).concatMapEager(toJust, 0, Flowable.bufferSize()); } @Test(expected = IllegalArgumentException.class) public void testInvalidCapacityHint() { Flowable.just(1).concatMapEager(toJust, Flowable.bufferSize(), 0); } @Test @SuppressWarnings("unchecked") public void testBackpressure() { Flowable.concatArrayEager(Flowable.just(1), Flowable.just(1)).subscribe(tsBp); tsBp.assertNoErrors(); tsBp.assertNoValues(); tsBp.assertNotComplete(); tsBp.request(1); tsBp.assertValue(1); tsBp.assertNoErrors(); tsBp.assertNotComplete(); tsBp.request(1); tsBp.assertValues(1, 1); tsBp.assertNoErrors(); tsBp.assertComplete(); } @Test public void testAsynchronousRun() { Flowable.range(1, 2).concatMapEager(new Function<Integer, Flowable<Integer>>() { @Override public Flowable<Integer> apply(Integer t) { return Flowable.range(1, 1000).subscribeOn(Schedulers.computation()); } }).observeOn(Schedulers.single()) .test() .awaitDone(5, TimeUnit.SECONDS) .assertNoErrors() .assertValueCount(2000) .assertComplete(); } @Test public void testReentrantWork() { final PublishProcessor<Integer> subject = PublishProcessor.create(); final AtomicBoolean once = new AtomicBoolean(); subject.concatMapEager(new Function<Integer, Flowable<Integer>>() { @Override public Flowable<Integer> apply(Integer t) { return Flowable.just(t); } }) .doOnNext(new Consumer<Integer>() { @Override public void accept(Integer t) { if (once.compareAndSet(false, true)) { subject.onNext(2); } } }) .subscribe(ts); subject.onNext(1); ts.assertNoErrors(); ts.assertNotComplete(); ts.assertValues(1, 2); } @Test public void testPrefetchIsBounded() { final AtomicInteger count = new AtomicInteger(); TestSubscriber<Object> ts = TestSubscriber.create(0); Flowable.just(1).concatMapEager(new Function<Integer, Flowable<Integer>>() { @Override public Flowable<Integer> apply(Integer t) { return Flowable.range(1, Flowable.bufferSize() * 2) .doOnNext(new Consumer<Integer>() { @Override public void accept(Integer t) { count.getAndIncrement(); } }).hide(); } }).subscribe(ts); ts.assertNoErrors(); ts.assertNoValues(); ts.assertNotComplete(); Assert.assertEquals(Flowable.bufferSize(), count.get()); } @Test @Ignore("Null values are not allowed in RS") public void testInnerNull() { Flowable.just(1).concatMapEager(new Function<Integer, Flowable<Integer>>() { @Override public Flowable<Integer> apply(Integer t) { return Flowable.just(null); } }).subscribe(ts); ts.assertNoErrors(); ts.assertComplete(); ts.assertValue(null); } @Test public void testMaxConcurrent5() { final List<Long> requests = new ArrayList<Long>(); Flowable.range(1, 100).doOnRequest(new LongConsumer() { @Override public void accept(long reqCount) { requests.add(reqCount); } }).concatMapEager(toJust, 5, Flowable.bufferSize()).subscribe(ts); ts.assertNoErrors(); ts.assertValueCount(100); ts.assertComplete(); Assert.assertEquals(5, (long) requests.get(0)); Assert.assertEquals(1, (long) requests.get(1)); Assert.assertEquals(1, (long) requests.get(2)); Assert.assertEquals(1, (long) requests.get(3)); Assert.assertEquals(1, (long) requests.get(4)); Assert.assertEquals(1, (long) requests.get(5)); } @SuppressWarnings("unchecked") @Test @Ignore("Currently there are no 2-9 argument variants, use concatArrayEager()") public void many() throws Exception { for (int i = 2; i < 10; i++) { Class<?>[] clazz = new Class[i]; Arrays.fill(clazz, Flowable.class); Flowable<Integer>[] obs = new Flowable[i]; Arrays.fill(obs, Flowable.just(1)); Integer[] expected = new Integer[i]; Arrays.fill(expected, 1); Method m = Flowable.class.getMethod("concatEager", clazz); TestSubscriber<Integer> ts = TestSubscriber.create(); ((Flowable<Integer>)m.invoke(null, (Object[])obs)).subscribe(ts); ts.assertValues(expected); ts.assertNoErrors(); ts.assertComplete(); } } @SuppressWarnings("unchecked") @Test public void capacityHint() { Flowable<Integer> source = Flowable.just(1); TestSubscriber<Integer> ts = TestSubscriber.create(); Flowable.concatEager(Arrays.asList(source, source, source), 1, 1).subscribe(ts); ts.assertValues(1, 1, 1); ts.assertNoErrors(); ts.assertComplete(); } @Test public void flowable() { Flowable<Integer> source = Flowable.just(1); TestSubscriber<Integer> ts = TestSubscriber.create(); Flowable.concatEager(Flowable.just(source, source, source)).subscribe(ts); ts.assertValues(1, 1, 1); ts.assertNoErrors(); ts.assertComplete(); } @Test public void flowableCapacityHint() { Flowable<Integer> source = Flowable.just(1); TestSubscriber<Integer> ts = TestSubscriber.create(); Flowable.concatEager(Flowable.just(source, source, source), 1, 1).subscribe(ts); ts.assertValues(1, 1, 1); ts.assertNoErrors(); ts.assertComplete(); } @SuppressWarnings("unchecked") @Test public void badCapacityHint() throws Exception { Flowable<Integer> source = Flowable.just(1); try { Flowable.concatEager(Arrays.asList(source, source, source), 1, -99); } catch (IllegalArgumentException ex) { assertEquals("prefetch > 0 required but it was -99", ex.getMessage()); } } @SuppressWarnings({ "unchecked", "rawtypes" }) @Test public void mappingBadCapacityHint() throws Exception { Flowable<Integer> source = Flowable.just(1); try { Flowable.just(source, source, source).concatMapEager((Function)Functions.identity(), 10, -99); } catch (IllegalArgumentException ex) { assertEquals("prefetch > 0 required but it was -99", ex.getMessage()); } } @Test public void concatEagerZero() { Flowable.concatEager(Collections.<Flowable<Integer>>emptyList()) .test() .assertResult(); } @SuppressWarnings("unchecked") @Test public void concatEagerOne() { Flowable.concatEager(Arrays.asList(Flowable.just(1))) .test() .assertResult(1); } @SuppressWarnings("unchecked") @Test public void concatEagerTwo() { Flowable.concatEager(Arrays.asList(Flowable.just(1), Flowable.just(2))) .test() .assertResult(1, 2); } @Test public void Flowable() { Flowable<Integer> source = Flowable.just(1); TestSubscriber<Integer> ts = TestSubscriber.create(); Flowable.concatEager(Flowable.just(source, source, source)).subscribe(ts); ts.assertValues(1, 1, 1); ts.assertNoErrors(); ts.assertComplete(); } @Test public void ObservableCapacityHint() { Flowable<Integer> source = Flowable.just(1); TestSubscriber<Integer> ts = TestSubscriber.create(); Flowable.concatEager(Flowable.just(source, source, source), 1, 1).subscribe(ts); ts.assertValues(1, 1, 1); ts.assertNoErrors(); ts.assertComplete(); } @SuppressWarnings("unchecked") @Test public void concatEagerIterable() { Flowable.concatEager(Arrays.asList(Flowable.just(1), Flowable.just(2))) .test() .assertResult(1, 2); } @Test public void empty() { Flowable.<Integer>empty().hide().concatMapEager(new Function<Integer, Flowable<Integer>>() { @Override public Flowable<Integer> apply(Integer v) throws Exception { return Flowable.range(1, 2); } }) .test() .assertResult(); } @Test public void dispose() { TestHelper.checkDisposed(Flowable.just(1).hide().concatMapEager(new Function<Integer, Flowable<Integer>>() { @Override public Flowable<Integer> apply(Integer v) throws Exception { return Flowable.range(1, 2); } })); } @Test public void innerError() { Flowable.<Integer>just(1).hide().concatMapEager(new Function<Integer, Flowable<Integer>>() { @Override public Flowable<Integer> apply(Integer v) throws Exception { return Flowable.error(new TestException()); } }) .test() .assertFailure(TestException.class); } @Test public void innerOuterRace() { 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(); TestSubscriber<Integer> to = ps1.concatMapEager(new Function<Integer, Flowable<Integer>>() { @Override public Flowable<Integer> apply(Integer v) throws Exception { return ps2; } }).test(); final TestException ex1 = new TestException(); final TestException ex2 = new TestException(); ps1.onNext(1); Runnable r1 = new Runnable() { @Override public void run() { ps1.onError(ex1); } }; Runnable r2 = new Runnable() { @Override public void run() { ps2.onError(ex2); } }; TestHelper.race(r1, r2, Schedulers.single()); to.assertSubscribed().assertNoValues().assertNotComplete(); Throwable ex = to.errors().get(0); if (ex instanceof CompositeException) { List<Throwable> es = TestHelper.errorList(to); TestHelper.assertError(es, 0, TestException.class); TestHelper.assertError(es, 1, TestException.class); } else { to.assertError(TestException.class); if (!errors.isEmpty()) { TestHelper.assertUndeliverable(errors, 0, TestException.class); } } } finally { RxJavaPlugins.reset(); } } } @Test public void innerErrorMaxConcurrency() { Flowable.<Integer>just(1).hide().concatMapEager(new Function<Integer, Flowable<Integer>>() { @Override public Flowable<Integer> apply(Integer v) throws Exception { return Flowable.error(new TestException()); } }, 1, 128) .test() .assertFailure(TestException.class); } @Test public void innerCallableThrows() { Flowable.<Integer>just(1).hide().concatMapEager(new Function<Integer, Flowable<Integer>>() { @Override public Flowable<Integer> apply(Integer v) throws Exception { return Flowable.fromCallable(new Callable<Integer>() { @Override public Integer call() throws Exception { throw new TestException(); } }); } }) .test() .assertFailure(TestException.class); } @Test public void innerErrorAfterPoll() { final UnicastProcessor<Integer> us = UnicastProcessor.create(); us.onNext(1); TestSubscriber<Integer> to = new TestSubscriber<Integer>() { @Override public void onNext(Integer t) { super.onNext(t); us.onError(new TestException()); } }; Flowable.<Integer>just(1).hide() .concatMapEager(new Function<Integer, Flowable<Integer>>() { @Override public Flowable<Integer> apply(Integer v) throws Exception { return us; } }, 1, 128) .subscribe(to); to .assertFailure(TestException.class, 1); } @Test public void nextCancelRace() { for (int i = 0; i < 500; i++) { final PublishProcessor<Integer> ps1 = PublishProcessor.create(); final TestSubscriber<Integer> to = ps1.concatMapEager(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(1); } }; Runnable r2 = new Runnable() { @Override public void run() { to.cancel(); } }; TestHelper.race(r1, r2, Schedulers.single()); to.assertEmpty(); } } @Test public void mapperCancels() { final TestSubscriber<Integer> to = new TestSubscriber<Integer>(); Flowable.just(1).hide() .concatMapEager(new Function<Integer, Flowable<Integer>>() { @Override public Flowable<Integer> apply(Integer v) throws Exception { to.cancel(); return Flowable.never(); } }, 1, 128) .subscribe(to); to.assertEmpty(); } @Test public void innerErrorFused() { Flowable.<Integer>just(1).hide().concatMapEager(new Function<Integer, Flowable<Integer>>() { @Override public Flowable<Integer> apply(Integer v) throws Exception { return Flowable.range(1, 2).map(new Function<Integer, Integer>() { @Override public Integer apply(Integer v) throws Exception { throw new TestException(); } }); } }) .test() .assertFailure(TestException.class); } @Test public void fuseAndTake() { UnicastProcessor<Integer> us = UnicastProcessor.create(); us.onNext(1); us.onComplete(); us.concatMapEager(new Function<Integer, Flowable<Integer>>() { @Override public Flowable<Integer> apply(Integer v) throws Exception { return Flowable.just(1); } }) .take(1) .test() .assertResult(1); } @Test public void doubleOnSubscribe() { TestHelper.checkDoubleOnSubscribeFlowable(new Function<Flowable<Object>, Flowable<Object>>() { @Override public Flowable<Object> apply(Flowable<Object> o) throws Exception { return o.concatMapEager(new Function<Object, Flowable<Object>>() { @Override public Flowable<Object> apply(Object v) throws Exception { return Flowable.just(v); } }); } }); } @Test public void doubleOnError() { List<Throwable> errors = TestHelper.trackPluginErrors(); try { @SuppressWarnings("rawtypes") final Subscriber[] sub = { null }; new Flowable<Integer>() { @Override protected void subscribeActual(Subscriber<? super Integer> s) { sub[0] = s; s.onSubscribe(new BooleanSubscription()); s.onNext(1); s.onError(new TestException("First")); } } .concatMapEager(Functions.justFunction(Flowable.just(1))) .test() .assertFailureAndMessage(TestException.class, "First", 1); sub[0].onError(new TestException("Second")); TestHelper.assertUndeliverable(errors, 0, TestException.class, "Second"); } finally { RxJavaPlugins.reset(); } } @Test public void innerOverflow() { List<Throwable> errors = TestHelper.trackPluginErrors(); try { Flowable.just(1) .concatMapEager(new Function<Integer, Publisher<Integer>>() { @Override public Publisher<Integer> apply(Integer v) throws Exception { return new Flowable<Integer>() { @Override protected void subscribeActual(Subscriber<? super Integer> s) { s.onSubscribe(new BooleanSubscription()); s.onNext(1); s.onNext(2); s.onError(new TestException()); } }; } }, 1, 1) .test(0L) .assertFailure(MissingBackpressureException.class); TestHelper.assertUndeliverable(errors, 0, TestException.class); } finally { RxJavaPlugins.reset(); } } @Test public void unboundedIn() { int n = Flowable.bufferSize() * 2; Flowable.range(1, n) .concatMapEager(new Function<Integer, Publisher<Integer>>() { @Override public Publisher<Integer> apply(Integer v) throws Exception { return Flowable.just(1); } }, Integer.MAX_VALUE, 16) .test() .assertValueCount(n) .assertComplete() .assertNoErrors(); } @Test public void drainCancelRaceOnEmpty() { for (int i = 0; i < 500; i++) { final PublishProcessor<Integer> pp = PublishProcessor.create(); final TestSubscriber<Integer> ts = new TestSubscriber<Integer>(0L); Flowable.just(1) .concatMapEager(Functions.justFunction(pp)) .subscribe(ts); Runnable r1 = new Runnable() { @Override public void run() { pp.onComplete(); } }; Runnable r2 = new Runnable() { @Override public void run() { ts.cancel(); } }; TestHelper.race(r1, r2); } } @Test public void innerLong() { int n = Flowable.bufferSize() * 2; Flowable.just(1).hide() .concatMapEager(Functions.justFunction(Flowable.range(1, n).hide())) .rebatchRequests(1) .test() .assertValueCount(n) .assertComplete() .assertNoErrors(); } @Test public void oneDelayed() { Flowable.just(1, 2, 3, 4, 5) .concatMapEager(new Function<Integer, Flowable<Integer>>() { @Override public Flowable<Integer> apply(Integer i) throws Exception { return i == 3 ? Flowable.just(i) : Flowable .just(i) .delay(1, TimeUnit.MILLISECONDS, Schedulers.io()); } }) .observeOn(Schedulers.io()) .test() .awaitDone(5, TimeUnit.SECONDS) .assertResult(1, 2, 3, 4, 5) ; } }