/** * 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.subscribers; import static org.junit.Assert.*; import static org.mockito.Mockito.*; import java.util.List; import java.util.concurrent.atomic.AtomicReference; import org.junit.*; import org.mockito.Mockito; import org.reactivestreams.*; import io.reactivex.*; import io.reactivex.exceptions.*; import io.reactivex.internal.subscriptions.BooleanSubscription; import io.reactivex.plugins.RxJavaPlugins; public class SafeSubscriberTest { /** * Ensure onNext can not be called after onError. */ @Test public void testOnNextAfterOnError() { TestObservable t = new TestObservable(); Flowable<String> st = Flowable.unsafeCreate(t); Subscriber<String> w = TestHelper.mockSubscriber(); st.subscribe(new SafeSubscriber<String>(new TestSubscriber<String>(w))); t.sendOnNext("one"); t.sendOnError(new RuntimeException("bad")); t.sendOnNext("two"); verify(w, times(1)).onNext("one"); verify(w, times(1)).onError(any(Throwable.class)); verify(w, Mockito.never()).onNext("two"); } /** * Ensure onComplete can not be called after onError. */ @Test public void testOnCompletedAfterOnError() { TestObservable t = new TestObservable(); Flowable<String> st = Flowable.unsafeCreate(t); Subscriber<String> w = TestHelper.mockSubscriber(); st.subscribe(new SafeSubscriber<String>(new TestSubscriber<String>(w))); t.sendOnNext("one"); t.sendOnError(new RuntimeException("bad")); t.sendOnCompleted(); verify(w, times(1)).onNext("one"); verify(w, times(1)).onError(any(Throwable.class)); verify(w, Mockito.never()).onComplete(); } /** * Ensure onNext can not be called after onComplete. */ @Test public void testOnNextAfterOnCompleted() { TestObservable t = new TestObservable(); Flowable<String> st = Flowable.unsafeCreate(t); Subscriber<String> w = TestHelper.mockSubscriber(); st.subscribe(new SafeSubscriber<String>(new TestSubscriber<String>(w))); t.sendOnNext("one"); t.sendOnCompleted(); t.sendOnNext("two"); verify(w, times(1)).onNext("one"); verify(w, Mockito.never()).onNext("two"); verify(w, times(1)).onComplete(); verify(w, Mockito.never()).onError(any(Throwable.class)); } /** * Ensure onError can not be called after onComplete. */ @Test public void testOnErrorAfterOnCompleted() { TestObservable t = new TestObservable(); Flowable<String> st = Flowable.unsafeCreate(t); Subscriber<String> w = TestHelper.mockSubscriber(); st.subscribe(new SafeSubscriber<String>(new TestSubscriber<String>(w))); t.sendOnNext("one"); t.sendOnCompleted(); t.sendOnError(new RuntimeException("bad")); verify(w, times(1)).onNext("one"); verify(w, times(1)).onComplete(); verify(w, Mockito.never()).onError(any(Throwable.class)); } /** * An Observable that doesn't do the right thing on UnSubscribe/Error/etc in that it will keep sending events down the pipe regardless of what happens. */ private static class TestObservable implements Publisher<String> { Subscriber<? super String> observer; /* used to simulate subscription */ public void sendOnCompleted() { observer.onComplete(); } /* used to simulate subscription */ public void sendOnNext(String value) { observer.onNext(value); } /* used to simulate subscription */ public void sendOnError(Throwable e) { observer.onError(e); } @Override public void subscribe(Subscriber<? super String> observer) { this.observer = observer; observer.onSubscribe(new Subscription() { @Override public void cancel() { // going to do nothing to pretend I'm a bad Observable that keeps allowing events to be sent System.out.println("==> SynchronizeTest unsubscribe that does nothing!"); } @Override public void request(long n) { } }); } } @Test public void onNextFailure() { AtomicReference<Throwable> onError = new AtomicReference<Throwable>(); try { OBSERVER_ONNEXT_FAIL(onError).onNext("one"); fail("expects exception to be thrown"); } catch (Exception e) { assertNull(onError.get()); assertTrue(e instanceof SafeSubscriberTestException); assertEquals("onNextFail", e.getMessage()); } } @Test public void onNextFailureSafe() { AtomicReference<Throwable> onError = new AtomicReference<Throwable>(); try { SafeSubscriber<String> safeObserver = new SafeSubscriber<String>(OBSERVER_ONNEXT_FAIL(onError)); safeObserver.onSubscribe(new BooleanSubscription()); safeObserver.onNext("one"); assertNotNull(onError.get()); assertTrue(onError.get() instanceof SafeSubscriberTestException); assertEquals("onNextFail", onError.get().getMessage()); } catch (Exception e) { fail("expects exception to be passed to onError"); } } @Test public void onCompleteFailure() { AtomicReference<Throwable> onError = new AtomicReference<Throwable>(); try { OBSERVER_ONCOMPLETED_FAIL(onError).onComplete(); fail("expects exception to be thrown"); } catch (Exception e) { assertNull(onError.get()); assertTrue(e instanceof SafeSubscriberTestException); assertEquals("onCompleteFail", e.getMessage()); } } @Test public void onErrorFailure() { try { OBSERVER_ONERROR_FAIL().onError(new SafeSubscriberTestException("error!")); fail("expects exception to be thrown"); } catch (Exception e) { assertTrue(e instanceof SafeSubscriberTestException); assertEquals("onErrorFail", e.getMessage()); } } @Test @Ignore("Observers can't throw") public void onErrorFailureSafe() { try { new SafeSubscriber<String>(OBSERVER_ONERROR_FAIL()).onError(new SafeSubscriberTestException("error!")); fail("expects exception to be thrown"); } catch (Exception e) { e.printStackTrace(); assertTrue(e instanceof RuntimeException); assertEquals("Error occurred when trying to propagate error to Subscriber.onError", e.getMessage()); Throwable e2 = e.getCause(); assertTrue(e2 instanceof CompositeException); List<Throwable> innerExceptions = ((CompositeException) e2).getExceptions(); assertEquals(2, innerExceptions.size()); Throwable e3 = innerExceptions.get(0); assertTrue(e3 instanceof SafeSubscriberTestException); assertEquals("error!", e3.getMessage()); Throwable e4 = innerExceptions.get(1); assertTrue(e4 instanceof SafeSubscriberTestException); assertEquals("onErrorFail", e4.getMessage()); } } @Test @Ignore("Observers can't throw") public void onErrorNotImplementedFailureSafe() { try { new SafeSubscriber<String>(OBSERVER_ONERROR_NOTIMPLEMENTED()).onError(new SafeSubscriberTestException("error!")); fail("expects exception to be thrown"); } catch (Exception e) { // assertTrue(e instanceof OnErrorNotImplementedException); assertTrue(e.getCause() instanceof SafeSubscriberTestException); assertEquals("error!", e.getCause().getMessage()); } } @Test public void onNextOnErrorFailure() { try { OBSERVER_ONNEXT_ONERROR_FAIL().onNext("one"); fail("expects exception to be thrown"); } catch (Exception e) { e.printStackTrace(); assertTrue(e instanceof SafeSubscriberTestException); assertEquals("onNextFail", e.getMessage()); } } @Test @Ignore("Observers can't throw") public void onNextOnErrorFailureSafe() { try { new SafeSubscriber<String>(OBSERVER_ONNEXT_ONERROR_FAIL()).onNext("one"); fail("expects exception to be thrown"); } catch (Exception e) { e.printStackTrace(); assertTrue(e instanceof RuntimeException); assertEquals("Error occurred when trying to propagate error to Subscriber.onError", e.getMessage()); Throwable e2 = e.getCause(); assertTrue(e2 instanceof CompositeException); List<Throwable> innerExceptions = ((CompositeException) e2).getExceptions(); assertEquals(2, innerExceptions.size()); Throwable e3 = innerExceptions.get(0); assertTrue(e3 instanceof SafeSubscriberTestException); assertEquals("onNextFail", e3.getMessage()); Throwable e4 = innerExceptions.get(1); assertTrue(e4 instanceof SafeSubscriberTestException); assertEquals("onErrorFail", e4.getMessage()); } } static final Subscription THROWING_DISPOSABLE = new Subscription() { @Override public void cancel() { // break contract by throwing exception throw new SafeSubscriberTestException("failure from unsubscribe"); } @Override public void request(long n) { // ignored } }; @Test @Ignore("Observers can't throw") public void onCompleteSuccessWithUnsubscribeFailure() { Subscriber<String> o = OBSERVER_SUCCESS(); try { o.onSubscribe(THROWING_DISPOSABLE); new SafeSubscriber<String>(o).onComplete(); fail("expects exception to be thrown"); } catch (Exception e) { e.printStackTrace(); // FIXME no longer assertable // assertTrue(o.isUnsubscribed()); // assertTrue(e instanceof UnsubscribeFailedException); assertTrue(e.getCause() instanceof SafeSubscriberTestException); assertEquals("failure from unsubscribe", e.getMessage()); // expected since onError fails so SafeSubscriber can't help } } @Test @Ignore("Observers can't throw") public void onErrorSuccessWithUnsubscribeFailure() { AtomicReference<Throwable> onError = new AtomicReference<Throwable>(); Subscriber<String> o = OBSERVER_SUCCESS(onError); try { o.onSubscribe(THROWING_DISPOSABLE); new SafeSubscriber<String>(o).onError(new SafeSubscriberTestException("failed")); fail("we expect the unsubscribe failure to cause an exception to be thrown"); } catch (Exception e) { e.printStackTrace(); // FIXME no longer assertable // assertTrue(o.isUnsubscribed()); // we still expect onError to have received something before unsubscribe blew up assertNotNull(onError.get()); assertTrue(onError.get() instanceof SafeSubscriberTestException); assertEquals("failed", onError.get().getMessage()); // now assert the exception that was thrown RuntimeException onErrorFailedException = (RuntimeException) e; assertTrue(onErrorFailedException.getCause() instanceof SafeSubscriberTestException); assertEquals("failure from unsubscribe", e.getMessage()); } } @Test @Ignore("Observers can't throw") public void onErrorFailureWithUnsubscribeFailure() { Subscriber<String> o = OBSERVER_ONERROR_FAIL(); try { o.onSubscribe(THROWING_DISPOSABLE); new SafeSubscriber<String>(o).onError(new SafeSubscriberTestException("onError failure")); fail("expects exception to be thrown"); } catch (Exception e) { e.printStackTrace(); // FIXME no longer assertable // assertTrue(o.isUnsubscribed()); // assertions for what is expected for the actual failure propagated to onError which then fails assertTrue(e instanceof RuntimeException); assertEquals("Error occurred when trying to propagate error to Subscriber.onError and during unsubscription.", e.getMessage()); Throwable e2 = e.getCause(); assertTrue(e2 instanceof CompositeException); List<Throwable> innerExceptions = ((CompositeException) e2).getExceptions(); assertEquals(3, innerExceptions.size()); Throwable e3 = innerExceptions.get(0); assertTrue(e3 instanceof SafeSubscriberTestException); assertEquals("onError failure", e3.getMessage()); Throwable e4 = innerExceptions.get(1); assertTrue(e4 instanceof SafeSubscriberTestException); assertEquals("onErrorFail", e4.getMessage()); Throwable e5 = innerExceptions.get(2); assertTrue(e5 instanceof SafeSubscriberTestException); assertEquals("failure from unsubscribe", e5.getMessage()); } } @Test @Ignore("Observers can't throw") public void onErrorNotImplementedFailureWithUnsubscribeFailure() { Subscriber<String> o = OBSERVER_ONERROR_NOTIMPLEMENTED(); try { o.onSubscribe(THROWING_DISPOSABLE); new SafeSubscriber<String>(o).onError(new SafeSubscriberTestException("error!")); fail("expects exception to be thrown"); } catch (Exception e) { e.printStackTrace(); // FIXME no longer assertable // assertTrue(o.isUnsubscribed()); // assertions for what is expected for the actual failure propagated to onError which then fails assertTrue(e instanceof RuntimeException); assertEquals("Subscriber.onError not implemented and error while unsubscribing.", e.getMessage()); Throwable e2 = e.getCause(); assertTrue(e2 instanceof CompositeException); List<Throwable> innerExceptions = ((CompositeException) e2).getExceptions(); assertEquals(2, innerExceptions.size()); Throwable e3 = innerExceptions.get(0); assertTrue(e3 instanceof SafeSubscriberTestException); assertEquals("error!", e3.getMessage()); Throwable e4 = innerExceptions.get(1); assertTrue(e4 instanceof SafeSubscriberTestException); assertEquals("failure from unsubscribe", e4.getMessage()); } } private static Subscriber<String> OBSERVER_SUCCESS() { return new DefaultSubscriber<String>() { @Override public void onComplete() { } @Override public void onError(Throwable e) { } @Override public void onNext(String args) { } }; } private static Subscriber<String> OBSERVER_SUCCESS(final AtomicReference<Throwable> onError) { return new DefaultSubscriber<String>() { @Override public void onComplete() { } @Override public void onError(Throwable e) { onError.set(e); } @Override public void onNext(String args) { } }; } private static Subscriber<String> OBSERVER_ONNEXT_FAIL(final AtomicReference<Throwable> onError) { return new DefaultSubscriber<String>() { @Override public void onComplete() { } @Override public void onError(Throwable e) { onError.set(e); } @Override public void onNext(String args) { throw new SafeSubscriberTestException("onNextFail"); } }; } private static Subscriber<String> OBSERVER_ONNEXT_ONERROR_FAIL() { return new DefaultSubscriber<String>() { @Override public void onComplete() { } @Override public void onError(Throwable e) { throw new SafeSubscriberTestException("onErrorFail"); } @Override public void onNext(String args) { throw new SafeSubscriberTestException("onNextFail"); } }; } private static Subscriber<String> OBSERVER_ONERROR_FAIL() { return new DefaultSubscriber<String>() { @Override public void onComplete() { } @Override public void onError(Throwable e) { throw new SafeSubscriberTestException("onErrorFail"); } @Override public void onNext(String args) { } }; } private static Subscriber<String> OBSERVER_ONERROR_NOTIMPLEMENTED() { return new DefaultSubscriber<String>() { @Override public void onComplete() { } @Override public void onError(Throwable e) { throw new RuntimeException(e); // throw new OnErrorNotImplementedException(e); } @Override public void onNext(String args) { } }; } private static Subscriber<String> OBSERVER_ONCOMPLETED_FAIL(final AtomicReference<Throwable> onError) { return new DefaultSubscriber<String>() { @Override public void onComplete() { throw new SafeSubscriberTestException("onCompleteFail"); } @Override public void onError(Throwable e) { onError.set(e); } @Override public void onNext(String args) { } }; } @SuppressWarnings("serial") private static class SafeSubscriberTestException extends RuntimeException { SafeSubscriberTestException(String message) { super(message); } } @Test @Ignore("Observers can't throw") public void testOnCompletedThrows() { final AtomicReference<Throwable> error = new AtomicReference<Throwable>(); SafeSubscriber<Integer> s = new SafeSubscriber<Integer>(new DefaultSubscriber<Integer>() { @Override public void onNext(Integer t) { } @Override public void onError(Throwable e) { error.set(e); } @Override public void onComplete() { throw new TestException(); } }); try { s.onComplete(); Assert.fail(); } catch (RuntimeException e) { assertNull(error.get()); } } @Test public void testActual() { Subscriber<Integer> actual = new DefaultSubscriber<Integer>() { @Override public void onNext(Integer t) { } @Override public void onError(Throwable e) { } @Override public void onComplete() { } }; SafeSubscriber<Integer> s = new SafeSubscriber<Integer>(actual); assertSame(actual, s.actual); } @Test public void dispose() { TestSubscriber<Integer> ts = new TestSubscriber<Integer>(); SafeSubscriber<Integer> so = new SafeSubscriber<Integer>(ts); BooleanSubscription d = new BooleanSubscription(); so.onSubscribe(d); ts.dispose(); assertTrue(d.isCancelled()); // assertTrue(so.isDisposed()); } @Test public void onNextAfterComplete() { TestSubscriber<Integer> ts = new TestSubscriber<Integer>(); SafeSubscriber<Integer> so = new SafeSubscriber<Integer>(ts); BooleanSubscription d = new BooleanSubscription(); so.onSubscribe(d); so.onComplete(); so.onNext(1); so.onError(new TestException()); so.onComplete(); ts.assertResult(); } @Test public void onNextNull() { TestSubscriber<Integer> ts = new TestSubscriber<Integer>(); SafeSubscriber<Integer> so = new SafeSubscriber<Integer>(ts); BooleanSubscription d = new BooleanSubscription(); so.onSubscribe(d); so.onNext(null); ts.assertFailure(NullPointerException.class); } @Test public void onNextWithoutOnSubscribe() { TestSubscriber<Integer> ts = new TestSubscriber<Integer>(); SafeSubscriber<Integer> so = new SafeSubscriber<Integer>(ts); so.onNext(1); ts.assertFailureAndMessage(NullPointerException.class, "Subscription not set!"); } @Test public void onErrorWithoutOnSubscribe() { TestSubscriber<Integer> ts = new TestSubscriber<Integer>(); SafeSubscriber<Integer> so = new SafeSubscriber<Integer>(ts); so.onError(new TestException()); ts.assertFailure(CompositeException.class); TestHelper.assertError(ts, 0, TestException.class); TestHelper.assertError(ts, 1, NullPointerException.class, "Subscription not set!"); } @Test public void onCompleteWithoutOnSubscribe() { TestSubscriber<Integer> ts = new TestSubscriber<Integer>(); SafeSubscriber<Integer> so = new SafeSubscriber<Integer>(ts); so.onComplete(); ts.assertFailureAndMessage(NullPointerException.class, "Subscription not set!"); } @Test public void onNextNormal() { TestSubscriber<Integer> ts = new TestSubscriber<Integer>(); SafeSubscriber<Integer> so = new SafeSubscriber<Integer>(ts); BooleanSubscription d = new BooleanSubscription(); so.onSubscribe(d); so.onNext(1); so.onComplete(); ts.assertResult(1); } static final class CrashDummy implements FlowableSubscriber<Object>, Subscription { final boolean crashOnSubscribe; int crashOnNext; final boolean crashOnError; final boolean crashOnComplete; final boolean crashDispose; final boolean crashRequest; Throwable error; CrashDummy(boolean crashOnSubscribe, int crashOnNext, boolean crashOnError, boolean crashOnComplete, boolean crashDispose, boolean crashRequest) { this.crashOnSubscribe = crashOnSubscribe; this.crashOnNext = crashOnNext; this.crashOnError = crashOnError; this.crashOnComplete = crashOnComplete; this.crashDispose = crashDispose; this.crashRequest = crashRequest; } @Override public void cancel() { if (crashDispose) { throw new TestException("cancel()"); } } @Override public void request(long n) { if (crashRequest) { throw new TestException("request()"); } } @Override public void onSubscribe(Subscription s) { if (crashOnSubscribe) { throw new TestException("onSubscribe()"); } } @Override public void onNext(Object value) { if (--crashOnNext == 0) { throw new TestException("onNext(" + value + ")"); } } @Override public void onError(Throwable e) { if (crashOnError) { throw new TestException("onError(" + e + ")"); } error = e; } @Override public void onComplete() { if (crashOnComplete) { throw new TestException("onComplete()"); } } public SafeSubscriber<Object> toSafe() { return new SafeSubscriber<Object>(this); } public CrashDummy assertError(Class<? extends Throwable> clazz) { if (!clazz.isInstance(error)) { throw new AssertionError("Different error: " + error); } return this; } public CrashDummy assertInnerError(int index, Class<? extends Throwable> clazz) { List<Throwable> cel = TestHelper.compositeList(error); TestHelper.assertError(cel, index, clazz); return this; } public CrashDummy assertInnerError(int index, Class<? extends Throwable> clazz, String message) { List<Throwable> cel = TestHelper.compositeList(error); TestHelper.assertError(cel, index, clazz, message); return this; } } @Test public void onNextOnErrorCrash() { List<Throwable> list = TestHelper.trackPluginErrors(); try { CrashDummy cd = new CrashDummy(false, 1, true, false, false, false); SafeSubscriber<Object> so = cd.toSafe(); so.onSubscribe(cd); so.onNext(1); TestHelper.assertError(list, 0, CompositeException.class); List<Throwable> ce = TestHelper.compositeList(list.get(0)); TestHelper.assertError(ce, 0, TestException.class, "onNext(1)"); TestHelper.assertError(ce, 1, TestException.class, "onError(io.reactivex.exceptions.TestException: onNext(1))"); } finally { RxJavaPlugins.reset(); } } @Test public void onNextDisposeCrash() { CrashDummy cd = new CrashDummy(false, 1, false, false, true, false); SafeSubscriber<Object> so = cd.toSafe(); so.onSubscribe(cd); so.onNext(1); cd.assertError(CompositeException.class); cd.assertInnerError(0, TestException.class, "onNext(1)"); cd.assertInnerError(1, TestException.class, "cancel()"); } @Test public void onSubscribeTwice() { List<Throwable> list = TestHelper.trackPluginErrors(); try { CrashDummy cd = new CrashDummy(false, 1, false, false, false, false); SafeSubscriber<Object> so = cd.toSafe(); so.onSubscribe(cd); so.onSubscribe(cd); TestHelper.assertError(list, 0, IllegalStateException.class); } finally { RxJavaPlugins.reset(); } } @Test public void onSubscribeCrashes() { List<Throwable> list = TestHelper.trackPluginErrors(); try { CrashDummy cd = new CrashDummy(true, 1, false, false, false, false); SafeSubscriber<Object> so = cd.toSafe(); so.onSubscribe(cd); TestHelper.assertUndeliverable(list, 0, TestException.class, "onSubscribe()"); } finally { RxJavaPlugins.reset(); } } @Test public void onSubscribeAndDisposeCrashes() { List<Throwable> list = TestHelper.trackPluginErrors(); try { CrashDummy cd = new CrashDummy(true, 1, false, false, true, false); SafeSubscriber<Object> so = cd.toSafe(); so.onSubscribe(cd); TestHelper.assertError(list, 0, CompositeException.class); List<Throwable> ce = TestHelper.compositeList(list.get(0)); TestHelper.assertError(ce, 0, TestException.class, "onSubscribe()"); TestHelper.assertError(ce, 1, TestException.class, "cancel()"); } finally { RxJavaPlugins.reset(); } } @Test public void onNextOnSubscribeCrash() { List<Throwable> list = TestHelper.trackPluginErrors(); try { CrashDummy cd = new CrashDummy(true, 1, false, false, false, false); SafeSubscriber<Object> so = cd.toSafe(); so.onNext(1); TestHelper.assertError(list, 0, CompositeException.class); List<Throwable> ce = TestHelper.compositeList(list.get(0)); TestHelper.assertError(ce, 0, NullPointerException.class, "Subscription not set!"); TestHelper.assertError(ce, 1, TestException.class, "onSubscribe()"); } finally { RxJavaPlugins.reset(); } } @Test public void onNextNullDisposeCrashes() { CrashDummy cd = new CrashDummy(false, 1, false, false, true, false); SafeSubscriber<Object> so = cd.toSafe(); so.onSubscribe(cd); so.onNext(null); cd.assertInnerError(0, NullPointerException.class); cd.assertInnerError(1, TestException.class, "cancel()"); } @Test public void noSubscribeOnErrorCrashes() { List<Throwable> list = TestHelper.trackPluginErrors(); try { CrashDummy cd = new CrashDummy(false, 1, true, false, false, false); SafeSubscriber<Object> so = cd.toSafe(); so.onNext(1); TestHelper.assertError(list, 0, CompositeException.class); List<Throwable> ce = TestHelper.compositeList(list.get(0)); TestHelper.assertError(ce, 0, NullPointerException.class, "Subscription not set!"); TestHelper.assertError(ce, 1, TestException.class, "onError(java.lang.NullPointerException: Subscription not set!)"); } finally { RxJavaPlugins.reset(); } } @Test public void onErrorNull() { CrashDummy cd = new CrashDummy(false, 1, false, false, false, false); SafeSubscriber<Object> so = cd.toSafe(); so.onSubscribe(cd); so.onError(null); cd.assertError(NullPointerException.class); } @Test public void onErrorNoSubscribeCrash() { List<Throwable> list = TestHelper.trackPluginErrors(); try { CrashDummy cd = new CrashDummy(true, 1, false, false, false, false); SafeSubscriber<Object> so = cd.toSafe(); so.onError(new TestException()); TestHelper.assertError(list, 0, CompositeException.class); List<Throwable> ce = TestHelper.compositeList(list.get(0)); TestHelper.assertError(ce, 0, TestException.class); TestHelper.assertError(ce, 1, NullPointerException.class, "Subscription not set!"); } finally { RxJavaPlugins.reset(); } } @Test public void onErrorNoSubscribeOnErrorCrash() { List<Throwable> list = TestHelper.trackPluginErrors(); try { CrashDummy cd = new CrashDummy(false, 1, true, false, false, false); SafeSubscriber<Object> so = cd.toSafe(); so.onError(new TestException()); TestHelper.assertError(list, 0, CompositeException.class); List<Throwable> ce = TestHelper.compositeList(list.get(0)); TestHelper.assertError(ce, 0, TestException.class); TestHelper.assertError(ce, 1, NullPointerException.class, "Subscription not set!"); TestHelper.assertError(ce, 2, TestException.class); } finally { RxJavaPlugins.reset(); } } @Test public void onCompleteteCrash() { List<Throwable> list = TestHelper.trackPluginErrors(); try { CrashDummy cd = new CrashDummy(false, 1, false, true, false, false); SafeSubscriber<Object> so = cd.toSafe(); so.onSubscribe(cd); so.onComplete(); TestHelper.assertUndeliverable(list, 0, TestException.class, "onComplete()"); } finally { RxJavaPlugins.reset(); } } @Test public void onCompleteteNoSubscribeCrash() { List<Throwable> list = TestHelper.trackPluginErrors(); try { CrashDummy cd = new CrashDummy(true, 1, false, true, false, false); SafeSubscriber<Object> so = cd.toSafe(); so.onComplete(); TestHelper.assertError(list, 0, CompositeException.class); List<Throwable> ce = TestHelper.compositeList(list.get(0)); TestHelper.assertError(ce, 0, NullPointerException.class, "Subscription not set!"); TestHelper.assertError(ce, 1, TestException.class, "onSubscribe()"); } finally { RxJavaPlugins.reset(); } } @Test public void onCompleteteNoSubscribeOnErrorCrash() { List<Throwable> list = TestHelper.trackPluginErrors(); try { CrashDummy cd = new CrashDummy(false, 1, true, true, false, false); SafeSubscriber<Object> so = cd.toSafe(); so.onComplete(); TestHelper.assertError(list, 0, CompositeException.class); List<Throwable> ce = TestHelper.compositeList(list.get(0)); TestHelper.assertError(ce, 0, NullPointerException.class, "Subscription not set!"); TestHelper.assertError(ce, 1, TestException.class); } finally { RxJavaPlugins.reset(); } } @Test public void requestCrash() { List<Throwable> list = TestHelper.trackPluginErrors(); try { CrashDummy cd = new CrashDummy(false, 1, false, false, false, true); SafeSubscriber<Object> so = cd.toSafe(); so.onSubscribe(cd); so.request(1); TestHelper.assertUndeliverable(list, 0, TestException.class); } finally { RxJavaPlugins.reset(); } } @Test public void cancelCrash() { List<Throwable> list = TestHelper.trackPluginErrors(); try { CrashDummy cd = new CrashDummy(false, 1, false, false, true, false); SafeSubscriber<Object> so = cd.toSafe(); so.onSubscribe(cd); so.cancel(); TestHelper.assertUndeliverable(list, 0, TestException.class); } finally { RxJavaPlugins.reset(); } } @Test public void requestCancelCrash() { List<Throwable> list = TestHelper.trackPluginErrors(); try { CrashDummy cd = new CrashDummy(false, 1, false, false, true, true); SafeSubscriber<Object> so = cd.toSafe(); so.onSubscribe(cd); so.request(1); TestHelper.assertError(list, 0, CompositeException.class); List<Throwable> ce = TestHelper.compositeList(list.get(0)); TestHelper.assertError(ce, 0, TestException.class, "request()"); TestHelper.assertError(ce, 1, TestException.class, "cancel()"); } finally { RxJavaPlugins.reset(); } } }