/** * 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.Mockito.*; import java.lang.reflect.Method; import java.util.*; import java.util.concurrent.*; import org.junit.*; import org.mockito.InOrder; import org.reactivestreams.*; import io.reactivex.*; import io.reactivex.Flowable; import io.reactivex.exceptions.*; import io.reactivex.functions.LongConsumer; import io.reactivex.internal.subscriptions.BooleanSubscription; import io.reactivex.processors.PublishProcessor; import io.reactivex.subscribers.*; public class FlowableMergeDelayErrorTest { Subscriber<String> stringObserver; @Before public void before() { stringObserver = TestHelper.mockSubscriber(); } @Test public void testErrorDelayed1() { final Flowable<String> o1 = Flowable.unsafeCreate(new TestErrorFlowable("four", null, "six")); // we expect to lose "six" from the source (and it should never be sent by the source since onError was called final Flowable<String> o2 = Flowable.unsafeCreate(new TestErrorFlowable("one", "two", "three")); Flowable<String> m = Flowable.mergeDelayError(o1, o2); m.subscribe(stringObserver); verify(stringObserver, times(1)).onError(any(NullPointerException.class)); verify(stringObserver, never()).onComplete(); verify(stringObserver, times(1)).onNext("one"); verify(stringObserver, times(1)).onNext("two"); verify(stringObserver, times(1)).onNext("three"); verify(stringObserver, times(1)).onNext("four"); verify(stringObserver, times(0)).onNext("five"); // despite not expecting it ... we don't do anything to prevent it if the source Flowable keeps sending after onError // inner Flowable errors are considered terminal for that source // verify(stringObserver, times(1)).onNext("six"); // inner Flowable errors are considered terminal for that source } @Test public void testErrorDelayed2() { final Flowable<String> o1 = Flowable.unsafeCreate(new TestErrorFlowable("one", "two", "three")); final Flowable<String> o2 = Flowable.unsafeCreate(new TestErrorFlowable("four", null, "six")); // we expect to lose "six" from the source (and it should never be sent by the source since onError was called final Flowable<String> o3 = Flowable.unsafeCreate(new TestErrorFlowable("seven", "eight", null)); final Flowable<String> o4 = Flowable.unsafeCreate(new TestErrorFlowable("nine")); Flowable<String> m = Flowable.mergeDelayError(o1, o2, o3, o4); m.subscribe(stringObserver); verify(stringObserver, times(1)).onError(any(CompositeException.class)); verify(stringObserver, never()).onComplete(); verify(stringObserver, times(1)).onNext("one"); verify(stringObserver, times(1)).onNext("two"); verify(stringObserver, times(1)).onNext("three"); verify(stringObserver, times(1)).onNext("four"); verify(stringObserver, times(0)).onNext("five"); // despite not expecting it ... we don't do anything to prevent it if the source Flowable keeps sending after onError // inner Flowable errors are considered terminal for that source // verify(stringObserver, times(1)).onNext("six"); verify(stringObserver, times(1)).onNext("seven"); verify(stringObserver, times(1)).onNext("eight"); verify(stringObserver, times(1)).onNext("nine"); } @Test public void testErrorDelayed3() { final Flowable<String> o1 = Flowable.unsafeCreate(new TestErrorFlowable("one", "two", "three")); final Flowable<String> o2 = Flowable.unsafeCreate(new TestErrorFlowable("four", "five", "six")); final Flowable<String> o3 = Flowable.unsafeCreate(new TestErrorFlowable("seven", "eight", null)); final Flowable<String> o4 = Flowable.unsafeCreate(new TestErrorFlowable("nine")); Flowable<String> m = Flowable.mergeDelayError(o1, o2, o3, o4); m.subscribe(stringObserver); verify(stringObserver, times(1)).onError(any(NullPointerException.class)); verify(stringObserver, never()).onComplete(); verify(stringObserver, times(1)).onNext("one"); verify(stringObserver, times(1)).onNext("two"); verify(stringObserver, times(1)).onNext("three"); verify(stringObserver, times(1)).onNext("four"); verify(stringObserver, times(1)).onNext("five"); verify(stringObserver, times(1)).onNext("six"); verify(stringObserver, times(1)).onNext("seven"); verify(stringObserver, times(1)).onNext("eight"); verify(stringObserver, times(1)).onNext("nine"); } @Test public void testErrorDelayed4() { final Flowable<String> o1 = Flowable.unsafeCreate(new TestErrorFlowable("one", "two", "three")); final Flowable<String> o2 = Flowable.unsafeCreate(new TestErrorFlowable("four", "five", "six")); final Flowable<String> o3 = Flowable.unsafeCreate(new TestErrorFlowable("seven", "eight")); final Flowable<String> o4 = Flowable.unsafeCreate(new TestErrorFlowable("nine", null)); Flowable<String> m = Flowable.mergeDelayError(o1, o2, o3, o4); m.subscribe(stringObserver); verify(stringObserver, times(1)).onError(any(NullPointerException.class)); verify(stringObserver, never()).onComplete(); verify(stringObserver, times(1)).onNext("one"); verify(stringObserver, times(1)).onNext("two"); verify(stringObserver, times(1)).onNext("three"); verify(stringObserver, times(1)).onNext("four"); verify(stringObserver, times(1)).onNext("five"); verify(stringObserver, times(1)).onNext("six"); verify(stringObserver, times(1)).onNext("seven"); verify(stringObserver, times(1)).onNext("eight"); verify(stringObserver, times(1)).onNext("nine"); } @Test public void testErrorDelayed4WithThreading() { final TestAsyncErrorFlowable o1 = new TestAsyncErrorFlowable("one", "two", "three"); final TestAsyncErrorFlowable o2 = new TestAsyncErrorFlowable("four", "five", "six"); final TestAsyncErrorFlowable o3 = new TestAsyncErrorFlowable("seven", "eight"); // throw the error at the very end so no onComplete will be called after it final TestAsyncErrorFlowable o4 = new TestAsyncErrorFlowable("nine", null); Flowable<String> m = Flowable.mergeDelayError(Flowable.unsafeCreate(o1), Flowable.unsafeCreate(o2), Flowable.unsafeCreate(o3), Flowable.unsafeCreate(o4)); m.subscribe(stringObserver); try { o1.t.join(); o2.t.join(); o3.t.join(); o4.t.join(); } catch (InterruptedException e) { throw new RuntimeException(e); } verify(stringObserver, times(1)).onNext("one"); verify(stringObserver, times(1)).onNext("two"); verify(stringObserver, times(1)).onNext("three"); verify(stringObserver, times(1)).onNext("four"); verify(stringObserver, times(1)).onNext("five"); verify(stringObserver, times(1)).onNext("six"); verify(stringObserver, times(1)).onNext("seven"); verify(stringObserver, times(1)).onNext("eight"); verify(stringObserver, times(1)).onNext("nine"); verify(stringObserver, times(1)).onError(any(NullPointerException.class)); verify(stringObserver, never()).onComplete(); } @Test public void testCompositeErrorDelayed1() { final Flowable<String> o1 = Flowable.unsafeCreate(new TestErrorFlowable("four", null, "six")); // we expect to lose "six" from the source (and it should never be sent by the source since onError was called final Flowable<String> o2 = Flowable.unsafeCreate(new TestErrorFlowable("one", "two", null)); Flowable<String> m = Flowable.mergeDelayError(o1, o2); m.subscribe(stringObserver); verify(stringObserver, times(1)).onError(any(Throwable.class)); verify(stringObserver, never()).onComplete(); verify(stringObserver, times(1)).onNext("one"); verify(stringObserver, times(1)).onNext("two"); verify(stringObserver, times(0)).onNext("three"); verify(stringObserver, times(1)).onNext("four"); verify(stringObserver, times(0)).onNext("five"); // despite not expecting it ... we don't do anything to prevent it if the source Flowable keeps sending after onError // inner Flowable errors are considered terminal for that source // verify(stringObserver, times(1)).onNext("six"); } @Test public void testCompositeErrorDelayed2() { final Flowable<String> o1 = Flowable.unsafeCreate(new TestErrorFlowable("four", null, "six")); // we expect to lose "six" from the source (and it should never be sent by the source since onError was called final Flowable<String> o2 = Flowable.unsafeCreate(new TestErrorFlowable("one", "two", null)); Flowable<String> m = Flowable.mergeDelayError(o1, o2); CaptureObserver w = new CaptureObserver(); m.subscribe(w); assertNotNull(w.e); int size = ((CompositeException)w.e).size(); if (size != 2) { w.e.printStackTrace(); } assertEquals(2, size); // if (w.e instanceof CompositeException) { // assertEquals(2, ((CompositeException) w.e).getExceptions().size()); // w.e.printStackTrace(); // } else { // fail("Expecting CompositeException"); // } } /** * The unit tests below are from OperationMerge and should ensure the normal merge functionality is correct. */ @Test public void testMergeFlowableOfFlowables() { final Flowable<String> o1 = Flowable.unsafeCreate(new TestSynchronousFlowable()); final Flowable<String> o2 = Flowable.unsafeCreate(new TestSynchronousFlowable()); Flowable<Flowable<String>> FlowableOfFlowables = Flowable.unsafeCreate(new Publisher<Flowable<String>>() { @Override public void subscribe(Subscriber<? super Flowable<String>> observer) { observer.onSubscribe(new BooleanSubscription()); // simulate what would happen in a Flowable observer.onNext(o1); observer.onNext(o2); observer.onComplete(); } }); Flowable<String> m = Flowable.mergeDelayError(FlowableOfFlowables); m.subscribe(stringObserver); verify(stringObserver, never()).onError(any(Throwable.class)); verify(stringObserver, times(1)).onComplete(); verify(stringObserver, times(2)).onNext("hello"); } @Test public void testMergeArray() { final Flowable<String> o1 = Flowable.unsafeCreate(new TestSynchronousFlowable()); final Flowable<String> o2 = Flowable.unsafeCreate(new TestSynchronousFlowable()); Flowable<String> m = Flowable.mergeDelayError(o1, o2); m.subscribe(stringObserver); verify(stringObserver, never()).onError(any(Throwable.class)); verify(stringObserver, times(2)).onNext("hello"); verify(stringObserver, times(1)).onComplete(); } @Test public void testMergeList() { final Flowable<String> o1 = Flowable.unsafeCreate(new TestSynchronousFlowable()); final Flowable<String> o2 = Flowable.unsafeCreate(new TestSynchronousFlowable()); List<Flowable<String>> listOfFlowables = new ArrayList<Flowable<String>>(); listOfFlowables.add(o1); listOfFlowables.add(o2); Flowable<String> m = Flowable.mergeDelayError(Flowable.fromIterable(listOfFlowables)); m.subscribe(stringObserver); verify(stringObserver, never()).onError(any(Throwable.class)); verify(stringObserver, times(1)).onComplete(); verify(stringObserver, times(2)).onNext("hello"); } @Test public void testMergeArrayWithThreading() { final TestASynchronousFlowable o1 = new TestASynchronousFlowable(); final TestASynchronousFlowable o2 = new TestASynchronousFlowable(); Flowable<String> m = Flowable.mergeDelayError(Flowable.unsafeCreate(o1), Flowable.unsafeCreate(o2)); m.subscribe(stringObserver); try { o1.t.join(); o2.t.join(); } catch (InterruptedException e) { throw new RuntimeException(e); } verify(stringObserver, never()).onError(any(Throwable.class)); verify(stringObserver, times(2)).onNext("hello"); verify(stringObserver, times(1)).onComplete(); } @Test(timeout = 1000L) public void testSynchronousError() { final Flowable<Flowable<String>> o1 = Flowable.error(new RuntimeException("unit test")); final CountDownLatch latch = new CountDownLatch(1); Flowable.mergeDelayError(o1).subscribe(new DefaultSubscriber<String>() { @Override public void onComplete() { fail("Expected onError path"); } @Override public void onError(Throwable e) { latch.countDown(); } @Override public void onNext(String s) { fail("Expected onError path"); } }); try { latch.await(); } catch (InterruptedException ex) { fail("interrupted"); } } private static class TestSynchronousFlowable implements Publisher<String> { @Override public void subscribe(Subscriber<? super String> observer) { observer.onSubscribe(new BooleanSubscription()); observer.onNext("hello"); observer.onComplete(); } } private static class TestASynchronousFlowable implements Publisher<String> { Thread t; @Override public void subscribe(final Subscriber<? super String> observer) { observer.onSubscribe(new BooleanSubscription()); t = new Thread(new Runnable() { @Override public void run() { observer.onNext("hello"); observer.onComplete(); } }); t.start(); } } private static class TestErrorFlowable implements Publisher<String> { String[] valuesToReturn; TestErrorFlowable(String... values) { valuesToReturn = values; } @Override public void subscribe(Subscriber<? super String> observer) { observer.onSubscribe(new BooleanSubscription()); boolean errorThrown = false; for (String s : valuesToReturn) { if (s == null) { System.out.println("throwing exception"); observer.onError(new NullPointerException()); errorThrown = true; // purposefully not returning here so it will continue calling onNext // so that we also test that we handle bad sequences like this } else { observer.onNext(s); } } if (!errorThrown) { observer.onComplete(); } } } private static class TestAsyncErrorFlowable implements Publisher<String> { String[] valuesToReturn; TestAsyncErrorFlowable(String... values) { valuesToReturn = values; } Thread t; @Override public void subscribe(final Subscriber<? super String> observer) { observer.onSubscribe(new BooleanSubscription()); t = new Thread(new Runnable() { @Override public void run() { for (String s : valuesToReturn) { if (s == null) { System.out.println("throwing exception"); try { Thread.sleep(100); } catch (Throwable e) { } observer.onError(new NullPointerException()); return; } else { observer.onNext(s); } } System.out.println("subscription complete"); observer.onComplete(); } }); t.start(); } } private static class CaptureObserver extends DefaultSubscriber<String> { volatile Throwable e; @Override public void onComplete() { } @Override public void onError(Throwable e) { this.e = e; } @Override public void onNext(String args) { } } @Test @Ignore("Subscribers should not throw") public void testMergeSourceWhichDoesntPropagateExceptionBack() { Flowable<Integer> source = Flowable.unsafeCreate(new Publisher<Integer>() { @Override public void subscribe(Subscriber<? super Integer> t1) { t1.onSubscribe(new BooleanSubscription()); try { t1.onNext(0); } catch (Throwable swallow) { } t1.onNext(1); t1.onComplete(); } }); Flowable<Integer> result = Flowable.mergeDelayError(source, Flowable.just(2)); final Subscriber<Integer> o = TestHelper.mockSubscriber(); InOrder inOrder = inOrder(o); result.subscribe(new DefaultSubscriber<Integer>() { int calls; @Override public void onNext(Integer t) { if (calls++ == 0) { throw new TestException(); } o.onNext(t); } @Override public void onError(Throwable e) { o.onError(e); } @Override public void onComplete() { o.onComplete(); } }); /* * If the child onNext throws, why would we keep accepting values from * other sources? */ inOrder.verify(o).onNext(2); inOrder.verify(o, never()).onNext(0); inOrder.verify(o, never()).onNext(1); inOrder.verify(o, never()).onNext(anyInt()); inOrder.verify(o).onError(any(TestException.class)); verify(o, never()).onComplete(); } @Test public void testErrorInParentFlowable() { TestSubscriber<Integer> ts = new TestSubscriber<Integer>(); Flowable.mergeDelayError( Flowable.just(Flowable.just(1), Flowable.just(2)) .startWith(Flowable.<Integer> error(new RuntimeException())) ).subscribe(ts); ts.awaitTerminalEvent(); ts.assertTerminated(); ts.assertValues(1, 2); assertEquals(1, ts.errorCount()); } @Test public void testErrorInParentFlowableDelayed() throws Exception { for (int i = 0; i < 50; i++) { final TestASynchronous1sDelayedFlowable o1 = new TestASynchronous1sDelayedFlowable(); final TestASynchronous1sDelayedFlowable o2 = new TestASynchronous1sDelayedFlowable(); Flowable<Flowable<String>> parentFlowable = Flowable.unsafeCreate(new Publisher<Flowable<String>>() { @Override public void subscribe(Subscriber<? super Flowable<String>> op) { op.onSubscribe(new BooleanSubscription()); op.onNext(Flowable.unsafeCreate(o1)); op.onNext(Flowable.unsafeCreate(o2)); op.onError(new NullPointerException("throwing exception in parent")); } }); Subscriber<String> stringObserver = TestHelper.mockSubscriber(); TestSubscriber<String> ts = new TestSubscriber<String>(stringObserver); Flowable<String> m = Flowable.mergeDelayError(parentFlowable); m.subscribe(ts); System.out.println("testErrorInParentFlowableDelayed | " + i); ts.awaitTerminalEvent(2000, TimeUnit.MILLISECONDS); ts.assertTerminated(); verify(stringObserver, times(2)).onNext("hello"); verify(stringObserver, times(1)).onError(any(NullPointerException.class)); verify(stringObserver, never()).onComplete(); } } private static class TestASynchronous1sDelayedFlowable implements Publisher<String> { Thread t; @Override public void subscribe(final Subscriber<? super String> observer) { observer.onSubscribe(new BooleanSubscription()); t = new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(100); } catch (InterruptedException e) { observer.onError(e); } observer.onNext("hello"); observer.onComplete(); } }); t.start(); } } @Test public void testDelayErrorMaxConcurrent() { final List<Long> requests = new ArrayList<Long>(); Flowable<Integer> source = Flowable.mergeDelayError(Flowable.just( Flowable.just(1).hide(), Flowable.<Integer>error(new TestException())) .doOnRequest(new LongConsumer() { @Override public void accept(long t1) { requests.add(t1); } }), 1); TestSubscriber<Integer> ts = new TestSubscriber<Integer>(); source.subscribe(ts); ts.assertValue(1); ts.assertTerminated(); ts.assertError(TestException.class); assertEquals(Arrays.asList(1L, 1L, 1L), requests); } // This is pretty much a clone of testMergeList but with the overloaded MergeDelayError for Iterables @Test public void mergeIterable() { final Flowable<String> o1 = Flowable.unsafeCreate(new TestSynchronousFlowable()); final Flowable<String> o2 = Flowable.unsafeCreate(new TestSynchronousFlowable()); List<Flowable<String>> listOfFlowables = new ArrayList<Flowable<String>>(); listOfFlowables.add(o1); listOfFlowables.add(o2); Flowable<String> m = Flowable.mergeDelayError(listOfFlowables); m.subscribe(stringObserver); verify(stringObserver, never()).onError(any(Throwable.class)); verify(stringObserver, times(1)).onComplete(); verify(stringObserver, times(2)).onNext("hello"); } @SuppressWarnings("unchecked") @Test public void iterableMaxConcurrent() { TestSubscriber<Integer> ts = TestSubscriber.create(); PublishProcessor<Integer> ps1 = PublishProcessor.create(); PublishProcessor<Integer> ps2 = PublishProcessor.create(); Flowable.mergeDelayError(Arrays.asList(ps1, ps2), 1).subscribe(ts); assertTrue("ps1 has no subscribers?!", ps1.hasSubscribers()); assertFalse("ps2 has subscribers?!", ps2.hasSubscribers()); ps1.onNext(1); ps1.onComplete(); assertFalse("ps1 has subscribers?!", ps1.hasSubscribers()); assertTrue("ps2 has no subscribers?!", ps2.hasSubscribers()); ps2.onNext(2); ps2.onComplete(); ts.assertValues(1, 2); ts.assertNoErrors(); ts.assertComplete(); } @SuppressWarnings("unchecked") @Test public void iterableMaxConcurrentError() { TestSubscriber<Integer> ts = TestSubscriber.create(); PublishProcessor<Integer> ps1 = PublishProcessor.create(); PublishProcessor<Integer> ps2 = PublishProcessor.create(); Flowable.mergeDelayError(Arrays.asList(ps1, ps2), 1).subscribe(ts); assertTrue("ps1 has no subscribers?!", ps1.hasSubscribers()); assertFalse("ps2 has subscribers?!", ps2.hasSubscribers()); ps1.onNext(1); ps1.onError(new TestException()); assertFalse("ps1 has subscribers?!", ps1.hasSubscribers()); assertTrue("ps2 has no subscribers?!", ps2.hasSubscribers()); ps2.onNext(2); ps2.onError(new TestException()); ts.assertValues(1, 2); ts.assertError(CompositeException.class); ts.assertNotComplete(); CompositeException ce = (CompositeException)ts.errors().get(0); assertEquals(2, ce.getExceptions().size()); } @SuppressWarnings("unchecked") @Test @Ignore("No 2-9 parameter mergeDelayError() overloads") public void mergeMany() 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("mergeDelayError", clazz); TestSubscriber<Integer> ts = TestSubscriber.create(); ((Flowable<Integer>)m.invoke(null, (Object[])obs)).subscribe(ts); ts.assertValues(expected); ts.assertNoErrors(); ts.assertComplete(); } } static <T> Flowable<T> withError(Flowable<T> source) { return source.concatWith(Flowable.<T>error(new TestException())); } @SuppressWarnings("unchecked") @Test @Ignore("No 2-9 parameter mergeDelayError() overloads") public void mergeManyError() 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]; for (int j = 0; j < i; j++) { obs[j] = withError(Flowable.just(1)); } Integer[] expected = new Integer[i]; Arrays.fill(expected, 1); Method m = Flowable.class.getMethod("mergeDelayError", clazz); TestSubscriber<Integer> ts = TestSubscriber.create(); ((Flowable<Integer>)m.invoke(null, (Object[])obs)).subscribe(ts); ts.assertValues(expected); ts.assertError(CompositeException.class); ts.assertNotComplete(); CompositeException ce = (CompositeException)ts.errors().get(0); assertEquals(i, ce.getExceptions().size()); } } @Test public void array() { for (int i = 1; i < 100; i++) { @SuppressWarnings("unchecked") Flowable<Integer>[] sources = new Flowable[i]; Arrays.fill(sources, Flowable.just(1)); Integer[] expected = new Integer[i]; for (int j = 0; j < i; j++) { expected[j] = 1; } Flowable.mergeArrayDelayError(sources) .test() .assertResult(expected); } } @SuppressWarnings("unchecked") @Test public void mergeArrayDelayError() { Flowable.mergeArrayDelayError(Flowable.just(1), Flowable.just(2)) .test() .assertResult(1, 2); } @SuppressWarnings("unchecked") @Test public void mergeIterableDelayErrorWithError() { Flowable.mergeDelayError( Arrays.asList(Flowable.just(1).concatWith(Flowable.<Integer>error(new TestException())), Flowable.just(2))) .test() .assertFailure(TestException.class, 1, 2); } @Test public void mergeDelayError() { Flowable.mergeDelayError( Flowable.just(Flowable.just(1), Flowable.just(2))) .test() .assertResult(1, 2); } @Test public void mergeDelayErrorWithError() { Flowable.mergeDelayError( Flowable.just(Flowable.just(1).concatWith(Flowable.<Integer>error(new TestException())), Flowable.just(2))) .test() .assertFailure(TestException.class, 1, 2); } @Test public void mergeDelayErrorMaxConcurrency() { Flowable.mergeDelayError( Flowable.just(Flowable.just(1), Flowable.just(2)), 1) .test() .assertResult(1, 2); } @Test public void mergeDelayErrorWithErrorMaxConcurrency() { Flowable.mergeDelayError( Flowable.just(Flowable.just(1).concatWith(Flowable.<Integer>error(new TestException())), Flowable.just(2)), 1) .test() .assertFailure(TestException.class, 1, 2); } @SuppressWarnings("unchecked") @Test public void mergeIterableDelayErrorMaxConcurrency() { Flowable.mergeDelayError( Arrays.asList(Flowable.just(1), Flowable.just(2)), 1) .test() .assertResult(1, 2); } @SuppressWarnings("unchecked") @Test public void mergeIterableDelayErrorWithErrorMaxConcurrency() { Flowable.mergeDelayError( Arrays.asList(Flowable.just(1).concatWith(Flowable.<Integer>error(new TestException())), Flowable.just(2)), 1) .test() .assertFailure(TestException.class, 1, 2); } @Test public void mergeDelayError3() { Flowable.mergeDelayError( Flowable.just(1), Flowable.just(2), Flowable.just(3) ) .test() .assertResult(1, 2, 3); } @Test public void mergeDelayError3WithError() { Flowable.mergeDelayError( Flowable.just(1), Flowable.just(2).concatWith(Flowable.<Integer>error(new TestException())), Flowable.just(3) ) .test() .assertFailure(TestException.class, 1, 2, 3); } @SuppressWarnings("unchecked") @Test public void mergeIterableDelayError() { Flowable.mergeDelayError(Arrays.asList(Flowable.just(1), Flowable.just(2))) .test() .assertResult(1, 2); } }