/** * 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.plugins; import static org.junit.Assert.*; import java.io.*; import java.lang.Thread.UncaughtExceptionHandler; import java.lang.reflect.*; import java.util.*; import java.util.concurrent.*; import java.util.concurrent.atomic.*; import org.junit.*; import org.reactivestreams.*; import io.reactivex.*; import io.reactivex.Observable; import io.reactivex.Observer; import io.reactivex.Scheduler.Worker; import io.reactivex.disposables.*; import io.reactivex.exceptions.*; import io.reactivex.flowables.ConnectableFlowable; import io.reactivex.functions.*; import io.reactivex.internal.functions.Functions; import io.reactivex.internal.operators.completable.CompletableError; import io.reactivex.internal.operators.flowable.FlowableRange; import io.reactivex.internal.operators.maybe.MaybeError; import io.reactivex.internal.operators.observable.ObservableRange; import io.reactivex.internal.operators.parallel.ParallelFromPublisher; import io.reactivex.internal.operators.single.SingleJust; import io.reactivex.internal.schedulers.ImmediateThinScheduler; import io.reactivex.internal.subscriptions.ScalarSubscription; import io.reactivex.observables.ConnectableObservable; import io.reactivex.parallel.ParallelFlowable; import io.reactivex.schedulers.Schedulers; public class RxJavaPluginsTest { // static Observable<Integer> createObservable() { // return Observable.range(1, 5).map(new Function<Integer, Integer>() { // @Override // public Integer apply(Integer t) { // throw new TestException(); // } // }); // } // // static Flowable<Integer> createFlowable() { // return Flowable.range(1, 5).map(new Function<Integer, Integer>() { // @Override // public Integer apply(Integer t) { // throw new TestException(); // } // }); // } @Test public void constructorShouldBePrivate() { TestHelper.checkUtilityClass(RxJavaPlugins.class); } @Test @Ignore("Not part of 2.0") public void assemblyTrackingObservable() { // RxJavaPlugins.enableAssemblyTracking(); // try { // TestSubscriber<Integer> ts = TestSubscriber.create(); // // createObservable().subscribe(ts); // // ts.assertError(TestException.class); // // Throwable ex = ts.getOnErrorEvents().get(0); // // AssemblyStackTraceException aste = AssemblyStackTraceException.find(ex); // // assertNotNull(aste); // // assertTrue(aste.getMessage(), aste.getMessage().contains("createObservable")); // // RxJavaPlugins.clearAssemblyTracking(); // // ts = TestSubscriber.create(); // // createObservable().subscribe(ts); // // ts.assertError(TestException.class); // // ex = ts.getOnErrorEvents().get(0); // // aste = AssemblyStackTraceException.find(ex); // // assertNull(aste); // } finally { // RxJavaPlugins.resetAssemblyTracking(); // } } // static Single<Integer> createSingle() { // return Single.just(1).map(new Function<Integer, Integer>() { // @Override // public Integer apply(Integer t) { // throw new TestException(); // } // }); // } @Test @Ignore("Not part of 2.0") public void assemblyTrackingSingle() { // RxJavaPlugins.enableAssemblyTracking(); // try { // TestSubscriber<Integer> ts = TestSubscriber.create(); // // createSingle().subscribe(ts); // // ts.assertError(TestException.class); // // Throwable ex = ts.getOnErrorEvents().get(0); // // AssemblyStackTraceException aste = AssemblyStackTraceException.find(ex); // // assertNotNull(aste); // // assertTrue(aste.getMessage(), aste.getMessage().contains("createSingle")); // // RxJavaPlugins.clearAssemblyTracking(); // // ts = TestSubscriber.create(); // // createSingle().subscribe(ts); // // ts.assertError(TestException.class); // // ex = ts.getOnErrorEvents().get(0); // // aste = AssemblyStackTraceException.find(ex); // // assertNull(aste); // } finally { // RxJavaPlugins.resetAssemblyTracking(); // } } // static Completable createCompletable() { // return Completable.error(new Callable<Throwable>() { // @Override // public Throwable call() { // return new TestException(); // } // }); // } @Test @Ignore("Not part of 2.0") public void assemblyTrackingCompletable() { // RxJavaPlugins.enableAssemblyTracking(); // try { // TestSubscriber<Integer> ts = TestSubscriber.create(); // // createCompletable().subscribe(ts); // // ts.assertError(TestException.class); // // Throwable ex = ts.getOnErrorEvents().get(0); // // AssemblyStackTraceException aste = AssemblyStackTraceException.find(ex); // // assertNotNull(aste); // // assertTrue(aste.getMessage(), aste.getMessage().contains("createCompletable")); // // RxJavaPlugins.clearAssemblyTracking(); // // ts = TestSubscriber.create(); // // createCompletable().subscribe(ts); // // ts.assertError(TestException.class); // // ex = ts.getOnErrorEvents().get(0); // // aste = AssemblyStackTraceException.find(ex); // // assertNull(aste); // // } finally { // RxJavaPlugins.resetAssemblyTracking(); // } } @SuppressWarnings({ "rawtypes" }) @Test public void lockdown() throws Exception { RxJavaPlugins.reset(); RxJavaPlugins.lockdown(); try { assertTrue(RxJavaPlugins.isLockdown()); Consumer a1 = Functions.emptyConsumer(); Callable f0 = new Callable() { @Override public Object call() { return null; } }; Function f1 = Functions.identity(); BiFunction f2 = new BiFunction() { @Override public Object apply(Object t1, Object t2) { return t2; } }; BooleanSupplier bs = new BooleanSupplier() { @Override public boolean getAsBoolean() throws Exception { return true; } }; for (Method m : RxJavaPlugins.class.getMethods()) { if (m.getName().startsWith("set")) { Method getter; Class<?> paramType = m.getParameterTypes()[0]; if (paramType == Boolean.TYPE) { getter = RxJavaPlugins.class.getMethod("is" + m.getName().substring(3)); } else { getter = RxJavaPlugins.class.getMethod("get" + m.getName().substring(3)); } Object before = getter.invoke(null); try { if (paramType.isAssignableFrom(Boolean.TYPE)) { m.invoke(null, true); } else if (paramType.isAssignableFrom(Callable.class)) { m.invoke(null, f0); } else if (paramType.isAssignableFrom(Function.class)) { m.invoke(null, f1); } else if (paramType.isAssignableFrom(Consumer.class)) { m.invoke(null, a1); } else if (paramType.isAssignableFrom(BooleanSupplier.class)) { m.invoke(null, bs); } else { m.invoke(null, f2); } fail("Should have thrown InvocationTargetException(IllegalStateException)"); } catch (InvocationTargetException ex) { if (ex.getCause() instanceof IllegalStateException) { assertEquals("Plugins can't be changed anymore",ex.getCause().getMessage()); } else { fail("Should have thrown InvocationTargetException(IllegalStateException)"); } } Object after = getter.invoke(null); if (paramType.isPrimitive()) { assertEquals(m.toString(), before, after); } else { assertSame(m.toString(), before, after); } } } // Object o1 = RxJavaPlugins.getOnObservableCreate(); // Object o2 = RxJavaPlugins.getOnSingleCreate(); // Object o3 = RxJavaPlugins.getOnCompletableCreate(); // // RxJavaPlugins.enableAssemblyTracking(); // RxJavaPlugins.clearAssemblyTracking(); // RxJavaPlugins.resetAssemblyTracking(); // // // assertSame(o1, RxJavaPlugins.getOnObservableCreate()); // assertSame(o2, RxJavaPlugins.getOnSingleCreate()); // assertSame(o3, RxJavaPlugins.getOnCompletableCreate()); } finally { RxJavaPlugins.unlock(); RxJavaPlugins.reset(); assertFalse(RxJavaPlugins.isLockdown()); } } Function<Scheduler, Scheduler> replaceWithImmediate = new Function<Scheduler, Scheduler>() { @Override public Scheduler apply(Scheduler t) { return ImmediateThinScheduler.INSTANCE; } }; @Test public void overrideSingleScheduler() { try { RxJavaPlugins.setSingleSchedulerHandler(replaceWithImmediate); assertSame(ImmediateThinScheduler.INSTANCE, Schedulers.single()); } finally { RxJavaPlugins.reset(); } // make sure the reset worked assertNotSame(ImmediateThinScheduler.INSTANCE, Schedulers.single()); } @Test public void overrideComputationScheduler() { try { RxJavaPlugins.setComputationSchedulerHandler(replaceWithImmediate); assertSame(ImmediateThinScheduler.INSTANCE, Schedulers.computation()); } finally { RxJavaPlugins.reset(); } // make sure the reset worked assertNotSame(ImmediateThinScheduler.INSTANCE, Schedulers.computation()); } @Test public void overrideIoScheduler() { try { RxJavaPlugins.setIoSchedulerHandler(replaceWithImmediate); assertSame(ImmediateThinScheduler.INSTANCE, Schedulers.io()); } finally { RxJavaPlugins.reset(); } // make sure the reset worked assertNotSame(ImmediateThinScheduler.INSTANCE, Schedulers.io()); } @Test public void overrideNewThreadScheduler() { try { RxJavaPlugins.setNewThreadSchedulerHandler(replaceWithImmediate); assertSame(ImmediateThinScheduler.INSTANCE, Schedulers.newThread()); } finally { RxJavaPlugins.reset(); } // make sure the reset worked assertNotSame(ImmediateThinScheduler.INSTANCE, Schedulers.newThread()); } Function<Callable<Scheduler>, Scheduler> initReplaceWithImmediate = new Function<Callable<Scheduler>, Scheduler>() { @Override public Scheduler apply(Callable<Scheduler> t) { return ImmediateThinScheduler.INSTANCE; } }; @Test public void overrideInitSingleScheduler() { final Scheduler s = Schedulers.single(); // make sure the Schedulers is initialized Callable<Scheduler> c = new Callable<Scheduler>() { @Override public Scheduler call() throws Exception { return s; } }; try { RxJavaPlugins.setInitSingleSchedulerHandler(initReplaceWithImmediate); assertSame(ImmediateThinScheduler.INSTANCE, RxJavaPlugins.initSingleScheduler(c)); } finally { RxJavaPlugins.reset(); } // make sure the reset worked assertSame(s, RxJavaPlugins.initSingleScheduler(c)); } @Test public void overrideInitComputationScheduler() { final Scheduler s = Schedulers.computation(); // make sure the Schedulers is initialized Callable<Scheduler> c = new Callable<Scheduler>() { @Override public Scheduler call() throws Exception { return s; } }; try { RxJavaPlugins.setInitComputationSchedulerHandler(initReplaceWithImmediate); assertSame(ImmediateThinScheduler.INSTANCE, RxJavaPlugins.initComputationScheduler(c)); } finally { RxJavaPlugins.reset(); } // make sure the reset worked assertSame(s, RxJavaPlugins.initComputationScheduler(c)); } @Test public void overrideInitIoScheduler() { final Scheduler s = Schedulers.io(); // make sure the Schedulers is initialized; Callable<Scheduler> c = new Callable<Scheduler>() { @Override public Scheduler call() throws Exception { return s; } }; try { RxJavaPlugins.setInitIoSchedulerHandler(initReplaceWithImmediate); assertSame(ImmediateThinScheduler.INSTANCE, RxJavaPlugins.initIoScheduler(c)); } finally { RxJavaPlugins.reset(); } // make sure the reset worked assertSame(s, RxJavaPlugins.initIoScheduler(c)); } @Test public void overrideInitNewThreadScheduler() { final Scheduler s = Schedulers.newThread(); // make sure the Schedulers is initialized; Callable<Scheduler> c = new Callable<Scheduler>() { @Override public Scheduler call() throws Exception { return s; } }; try { RxJavaPlugins.setInitNewThreadSchedulerHandler(initReplaceWithImmediate); assertSame(ImmediateThinScheduler.INSTANCE, RxJavaPlugins.initNewThreadScheduler(c)); } finally { RxJavaPlugins.reset(); } // make sure the reset worked assertSame(s, RxJavaPlugins.initNewThreadScheduler(c)); } Callable<Scheduler> nullResultCallable = new Callable<Scheduler>() { @Override public Scheduler call() throws Exception { return null; } }; @Test public void overrideInitSingleSchedulerCrashes() { // fail when Callable is null try { RxJavaPlugins.initSingleScheduler(null); fail("Should have thrown NullPointerException"); } catch (NullPointerException npe) { assertEquals("Scheduler Callable can't be null", npe.getMessage()); } // fail when Callable result is null try { RxJavaPlugins.initSingleScheduler(nullResultCallable); fail("Should have thrown NullPointerException"); } catch (NullPointerException npe) { assertEquals("Scheduler Callable result can't be null", npe.getMessage()); } } @Test public void overrideInitComputationSchedulerCrashes() { // fail when Callable is null try { RxJavaPlugins.initComputationScheduler(null); fail("Should have thrown NullPointerException"); } catch (NullPointerException npe) { assertEquals("Scheduler Callable can't be null", npe.getMessage()); } // fail when Callable result is null try { RxJavaPlugins.initComputationScheduler(nullResultCallable); fail("Should have thrown NullPointerException"); } catch (NullPointerException npe) { assertEquals("Scheduler Callable result can't be null", npe.getMessage()); } } @Test public void overrideInitIoSchedulerCrashes() { // fail when Callable is null try { RxJavaPlugins.initIoScheduler(null); fail("Should have thrown NullPointerException"); } catch (NullPointerException npe) { assertEquals("Scheduler Callable can't be null", npe.getMessage()); } // fail when Callable result is null try { RxJavaPlugins.initIoScheduler(nullResultCallable); fail("Should have thrown NullPointerException"); } catch (NullPointerException npe) { assertEquals("Scheduler Callable result can't be null", npe.getMessage()); } } @Test public void overrideInitNewThreadSchedulerCrashes() { // fail when Callable is null try { RxJavaPlugins.initNewThreadScheduler(null); fail("Should have thrown NullPointerException"); } catch (NullPointerException npe) { // expected assertEquals("Scheduler Callable can't be null", npe.getMessage()); } // fail when Callable result is null try { RxJavaPlugins.initNewThreadScheduler(nullResultCallable); fail("Should have thrown NullPointerException"); } catch (NullPointerException npe) { assertEquals("Scheduler Callable result can't be null", npe.getMessage()); } } Callable<Scheduler> unsafeDefault = new Callable<Scheduler>() { @Override public Scheduler call() throws Exception { throw new AssertionError("Default Scheduler instance should not have been evaluated"); } }; @Test public void testDefaultSingleSchedulerIsInitializedLazily() { // unsafe default Scheduler Callable should not be evaluated try { RxJavaPlugins.setInitSingleSchedulerHandler(initReplaceWithImmediate); RxJavaPlugins.initSingleScheduler(unsafeDefault); } finally { RxJavaPlugins.reset(); } // make sure the reset worked assertNotSame(ImmediateThinScheduler.INSTANCE, Schedulers.single()); } @Test public void testDefaultIoSchedulerIsInitializedLazily() { // unsafe default Scheduler Callable should not be evaluated try { RxJavaPlugins.setInitIoSchedulerHandler(initReplaceWithImmediate); RxJavaPlugins.initIoScheduler(unsafeDefault); } finally { RxJavaPlugins.reset(); } // make sure the reset worked assertNotSame(ImmediateThinScheduler.INSTANCE, Schedulers.io()); } @Test public void testDefaultComputationSchedulerIsInitializedLazily() { // unsafe default Scheduler Callable should not be evaluated try { RxJavaPlugins.setInitComputationSchedulerHandler(initReplaceWithImmediate); RxJavaPlugins.initComputationScheduler(unsafeDefault); } finally { RxJavaPlugins.reset(); } // make sure the reset worked assertNotSame(ImmediateThinScheduler.INSTANCE, Schedulers.computation()); } @Test public void testDefaultNewThreadSchedulerIsInitializedLazily() { // unsafe default Scheduler Callable should not be evaluated try { RxJavaPlugins.setInitNewThreadSchedulerHandler(initReplaceWithImmediate); RxJavaPlugins.initNewThreadScheduler(unsafeDefault); } finally { RxJavaPlugins.reset(); } // make sure the reset worked assertNotSame(ImmediateThinScheduler.INSTANCE, Schedulers.newThread()); } @SuppressWarnings("rawtypes") @Test public void observableCreate() { try { RxJavaPlugins.setOnObservableAssembly(new Function<Observable, Observable>() { @Override public Observable apply(Observable t) { return new ObservableRange(1, 2); } }); Observable.range(10, 3) .test() .assertValues(1, 2) .assertNoErrors() .assertComplete(); } finally { RxJavaPlugins.reset(); } // make sure the reset worked Observable.range(10, 3) .test() .assertValues(10, 11, 12) .assertNoErrors() .assertComplete(); } @SuppressWarnings("rawtypes") @Test public void flowableCreate() { try { RxJavaPlugins.setOnFlowableAssembly(new Function<Flowable, Flowable>() { @Override public Flowable apply(Flowable t) { return new FlowableRange(1, 2); } }); Flowable.range(10, 3) .test() .assertValues(1, 2) .assertNoErrors() .assertComplete(); } finally { RxJavaPlugins.reset(); } // make sure the reset worked Flowable.range(10, 3) .test() .assertValues(10, 11, 12) .assertNoErrors() .assertComplete(); } @SuppressWarnings("rawtypes") @Test public void observableStart() { try { RxJavaPlugins.setOnObservableSubscribe(new BiFunction<Observable, Observer, Observer>() { @Override public Observer apply(Observable o, final Observer t) { return new Observer() { @Override public void onSubscribe(Disposable d) { t.onSubscribe(d); } @SuppressWarnings("unchecked") @Override public void onNext(Object value) { t.onNext((Integer)value - 9); } @Override public void onError(Throwable e) { t.onError(e); } @Override public void onComplete() { t.onComplete(); } }; } }); Observable.range(10, 3) .test() .assertValues(1, 2, 3) .assertNoErrors() .assertComplete(); } finally { RxJavaPlugins.reset(); } // make sure the reset worked Observable.range(10, 3) .test() .assertValues(10, 11, 12) .assertNoErrors() .assertComplete(); } @SuppressWarnings("rawtypes") @Test public void flowableStart() { try { RxJavaPlugins.setOnFlowableSubscribe(new BiFunction<Flowable, Subscriber, Subscriber>() { @Override public Subscriber apply(Flowable o, final Subscriber t) { return new Subscriber() { @Override public void onSubscribe(Subscription d) { t.onSubscribe(d); } @SuppressWarnings("unchecked") @Override public void onNext(Object value) { t.onNext((Integer)value - 9); } @Override public void onError(Throwable e) { t.onError(e); } @Override public void onComplete() { t.onComplete(); } }; } }); Flowable.range(10, 3) .test() .assertValues(1, 2, 3) .assertNoErrors() .assertComplete(); } finally { RxJavaPlugins.reset(); } // make sure the reset worked Flowable.range(10, 3) .test() .assertValues(10, 11, 12) .assertNoErrors() .assertComplete(); } @Test @Ignore("Different architecture, no longer supported") public void observableReturn() { // try { // final Subscription s = Subscriptions.empty(); // // RxJavaPlugins.setOnObservableReturn(new Function<Subscription, Subscription>() { // @Override // public Subscription call(Subscription t) { // return s; // } // }); // // TestSubscriber<Integer> ts = TestSubscriber.create(); // // Subscription u = Observable.range(10, 3).subscribe(ts); // // ts.assertValues(10, 11, 12); // ts.assertNoErrors(); // ts.assertComplete(); // // assertSame(s, u); // } finally { // RxJavaPlugins.reset(); // } } @SuppressWarnings("rawtypes") @Test public void singleCreate() { try { RxJavaPlugins.setOnSingleAssembly(new Function<Single, Single>() { @Override public Single apply(Single t) { return new SingleJust<Integer>(10); } }); Single.just(1) .test() .assertValue(10) .assertNoErrors() .assertComplete(); } finally { RxJavaPlugins.reset(); } // make sure the reset worked Single.just(1) .test() .assertValue(1) .assertNoErrors() .assertComplete(); } @SuppressWarnings("rawtypes") @Test public void singleStart() { try { RxJavaPlugins.setOnSingleSubscribe(new BiFunction<Single, SingleObserver, SingleObserver>() { @Override public SingleObserver apply(Single o, final SingleObserver t) { return new SingleObserver<Object>() { @Override public void onSubscribe(Disposable d) { t.onSubscribe(d); } @SuppressWarnings("unchecked") @Override public void onSuccess(Object value) { t.onSuccess(10); } @Override public void onError(Throwable e) { t.onError(e); } }; } }); Single.just(1) .test() .assertValue(10) .assertNoErrors() .assertComplete(); } finally { RxJavaPlugins.reset(); } // make sure the reset worked Single.just(1) .test() .assertValue(1) .assertNoErrors() .assertComplete(); } @Test @Ignore("Different architecture, no longer supported") public void singleReturn() { // try { // final Subscription s = Subscriptions.empty(); // // RxJavaPlugins.setOnSingleReturn(new Function<Subscription, Subscription>() { // @Override // public Subscription call(Subscription t) { // return s; // } // }); // // TestSubscriber<Integer> ts = TestSubscriber.create(); // // Subscription u = Single.just(1).subscribe(ts); // // ts.assertValue(1); // ts.assertNoErrors(); // ts.assertComplete(); // // assertSame(s, u); // } finally { // RxJavaPlugins.reset(); // } } @Test public void completableCreate() { try { RxJavaPlugins.setOnCompletableAssembly(new Function<Completable, Completable>() { @Override public Completable apply(Completable t) { return new CompletableError(new TestException()); } }); Completable.complete() .test() .assertNoValues() .assertNotComplete() .assertError(TestException.class); } finally { RxJavaPlugins.reset(); } // make sure the reset worked Completable.complete() .test() .assertNoValues() .assertNoErrors() .assertComplete(); } @Test public void completableStart() { try { RxJavaPlugins.setOnCompletableSubscribe(new BiFunction<Completable, CompletableObserver, CompletableObserver>() { @Override public CompletableObserver apply(Completable o, final CompletableObserver t) { return new CompletableObserver() { @Override public void onSubscribe(Disposable d) { t.onSubscribe(d); } @Override public void onError(Throwable e) { t.onError(e); } @Override public void onComplete() { t.onError(new TestException()); } }; } }); Completable.complete() .test() .assertNoValues() .assertNotComplete() .assertError(TestException.class); } finally { RxJavaPlugins.reset(); } // make sure the reset worked Completable.complete() .test() .assertNoValues() .assertNoErrors() .assertComplete(); } void onSchedule(Worker w) throws InterruptedException { try { try { final AtomicInteger value = new AtomicInteger(); final CountDownLatch cdl = new CountDownLatch(1); RxJavaPlugins.setScheduleHandler(new Function<Runnable, Runnable>() { @Override public Runnable apply(Runnable t) { return new Runnable() { @Override public void run() { value.set(10); cdl.countDown(); } }; } }); w.schedule(new Runnable() { @Override public void run() { value.set(1); cdl.countDown(); } }); cdl.await(); assertEquals(10, value.get()); } finally { RxJavaPlugins.reset(); } // make sure the reset worked final AtomicInteger value = new AtomicInteger(); final CountDownLatch cdl = new CountDownLatch(1); w.schedule(new Runnable() { @Override public void run() { value.set(1); cdl.countDown(); } }); cdl.await(); assertEquals(1, value.get()); } finally { w.dispose(); } } @Test public void onScheduleComputation() throws InterruptedException { onSchedule(Schedulers.computation().createWorker()); } @Test public void onScheduleIO() throws InterruptedException { onSchedule(Schedulers.io().createWorker()); } @Test public void onScheduleNewThread() throws InterruptedException { onSchedule(Schedulers.newThread().createWorker()); } @Test public void onError() { try { final List<Throwable> list = new ArrayList<Throwable>(); RxJavaPlugins.setErrorHandler(new Consumer<Throwable>() { @Override public void accept(Throwable t) { list.add(t); } }); RxJavaPlugins.onError(new TestException("Forced failure")); assertEquals(1, list.size()); assertUndeliverableTestException(list, 0, "Forced failure"); } finally { RxJavaPlugins.reset(); } } @Test @Ignore("No (need for) clear() method in 2.0") public void clear() throws Exception { // RxJavaPlugins.reset(); // try { // RxJavaPlugins.clear(); // for (Method m : RxJavaPlugins.class.getMethods()) { // if (m.getName().startsWith("getOn")) { // assertNull(m.toString(), m.invoke(null)); // } // } // // } finally { // RxJavaPlugins.reset(); // } // // for (Method m : RxJavaPlugins.class.getMethods()) { // if (m.getName().startsWith("getOn") // && !m.getName().endsWith("Scheduler") // && !m.getName().contains("GenericScheduledExecutorService")) { // assertNotNull(m.toString(), m.invoke(null)); // } // } } @Test public void onErrorNoHandler() { try { final List<Throwable> list = new ArrayList<Throwable>(); RxJavaPlugins.setErrorHandler(null); Thread.currentThread().setUncaughtExceptionHandler(new UncaughtExceptionHandler() { @Override public void uncaughtException(Thread t, Throwable e) { list.add(e); } }); RxJavaPlugins.onError(new TestException("Forced failure")); Thread.currentThread().setUncaughtExceptionHandler(null); // this will be printed on the console and should not crash RxJavaPlugins.onError(new TestException("Forced failure 3")); assertEquals(1, list.size()); assertUndeliverableTestException(list, 0, "Forced failure"); } finally { RxJavaPlugins.reset(); Thread.currentThread().setUncaughtExceptionHandler(null); } } @Test public void onErrorCrashes() { try { final List<Throwable> list = new ArrayList<Throwable>(); RxJavaPlugins.setErrorHandler(new Consumer<Throwable>() { @Override public void accept(Throwable t) { throw new TestException("Forced failure 2"); } }); Thread.currentThread().setUncaughtExceptionHandler(new UncaughtExceptionHandler() { @Override public void uncaughtException(Thread t, Throwable e) { list.add(e); } }); RxJavaPlugins.onError(new TestException("Forced failure")); assertEquals(2, list.size()); assertTestException(list, 0, "Forced failure 2"); assertUndeliverableTestException(list, 1, "Forced failure"); Thread.currentThread().setUncaughtExceptionHandler(null); } finally { RxJavaPlugins.reset(); Thread.currentThread().setUncaughtExceptionHandler(null); } } @Test public void onErrorWithNull() { try { final List<Throwable> list = new ArrayList<Throwable>(); RxJavaPlugins.setErrorHandler(new Consumer<Throwable>() { @Override public void accept(Throwable t) { throw new TestException("Forced failure 2"); } }); Thread.currentThread().setUncaughtExceptionHandler(new UncaughtExceptionHandler() { @Override public void uncaughtException(Thread t, Throwable e) { list.add(e); } }); RxJavaPlugins.onError(null); assertEquals(2, list.size()); assertTestException(list, 0, "Forced failure 2"); assertNPE(list, 1); RxJavaPlugins.reset(); RxJavaPlugins.onError(null); assertNPE(list, 2); } finally { RxJavaPlugins.reset(); Thread.currentThread().setUncaughtExceptionHandler(null); } } /** * Ensure set*() accepts a consumers/functions with wider bounds. * @throws Exception on error */ @Test @SuppressWarnings("rawtypes") public void onErrorWithSuper() throws Exception { try { Consumer<? super Throwable> errorHandler = new Consumer<Throwable>() { @Override public void accept(Throwable t) { throw new TestException("Forced failure 2"); } }; RxJavaPlugins.setErrorHandler(errorHandler); Consumer<? super Throwable> errorHandler1 = RxJavaPlugins.getErrorHandler(); assertSame(errorHandler, errorHandler1); Function<? super Scheduler, ? extends Scheduler> scheduler2scheduler = new Function<Scheduler, Scheduler>() { @Override public Scheduler apply(Scheduler scheduler) throws Exception { return scheduler; } }; Function<? super Callable<Scheduler>, ? extends Scheduler> callable2scheduler = new Function<Callable<Scheduler>, Scheduler>() { @Override public Scheduler apply(Callable<Scheduler> schedulerCallable) throws Exception { return schedulerCallable.call(); } }; Function<? super ConnectableFlowable, ? extends ConnectableFlowable> connectableFlowable2ConnectableFlowable = new Function<ConnectableFlowable, ConnectableFlowable>() { @Override public ConnectableFlowable apply(ConnectableFlowable connectableFlowable) throws Exception { return connectableFlowable; } }; Function<? super ConnectableObservable, ? extends ConnectableObservable> connectableObservable2ConnectableObservable = new Function<ConnectableObservable, ConnectableObservable>() { @Override public ConnectableObservable apply(ConnectableObservable connectableObservable) throws Exception { return connectableObservable; } }; Function<? super Flowable, ? extends Flowable> flowable2Flowable = new Function<Flowable, Flowable>() { @Override public Flowable apply(Flowable flowable) throws Exception { return flowable; } }; BiFunction<? super Flowable, ? super Subscriber, ? extends Subscriber> flowable2subscriber = new BiFunction<Flowable, Subscriber, Subscriber>() { @Override public Subscriber apply(Flowable flowable, Subscriber subscriber) throws Exception { return subscriber; } }; Function<Maybe, Maybe> maybe2maybe = new Function<Maybe, Maybe>() { @Override public Maybe apply(Maybe maybe) throws Exception { return maybe; } }; BiFunction<Maybe, MaybeObserver, MaybeObserver> maybe2observer = new BiFunction<Maybe, MaybeObserver, MaybeObserver>() { @Override public MaybeObserver apply(Maybe maybe, MaybeObserver maybeObserver) throws Exception { return maybeObserver; } }; Function<Observable, Observable> observable2observable = new Function<Observable, Observable>() { @Override public Observable apply(Observable observable) throws Exception { return observable; } }; BiFunction<? super Observable, ? super Observer, ? extends Observer> observable2observer = new BiFunction<Observable, Observer, Observer>() { @Override public Observer apply(Observable observable, Observer observer) throws Exception { return observer; } }; Function<? super ParallelFlowable, ? extends ParallelFlowable> parallelFlowable2parallelFlowable = new Function<ParallelFlowable, ParallelFlowable>() { @Override public ParallelFlowable apply(ParallelFlowable parallelFlowable) throws Exception { return parallelFlowable; } }; Function<Single, Single> single2single = new Function<Single, Single>() { @Override public Single apply(Single single) throws Exception { return single; } }; BiFunction<? super Single, ? super SingleObserver, ? extends SingleObserver> single2observer = new BiFunction<Single, SingleObserver, SingleObserver>() { @Override public SingleObserver apply(Single single, SingleObserver singleObserver) throws Exception { return singleObserver; } }; Function<? super Runnable, ? extends Runnable> runnable2runnable = new Function<Runnable, Runnable>() { @Override public Runnable apply(Runnable runnable) throws Exception { return runnable; } }; BiFunction<? super Completable, ? super CompletableObserver, ? extends CompletableObserver> completableObserver2completableObserver = new BiFunction<Completable, CompletableObserver, CompletableObserver>() { @Override public CompletableObserver apply(Completable completable, CompletableObserver completableObserver) throws Exception { return completableObserver; } }; Function<? super Completable, ? extends Completable> completable2completable = new Function<Completable, Completable>() { @Override public Completable apply(Completable completable) throws Exception { return completable; } }; RxJavaPlugins.setInitComputationSchedulerHandler(callable2scheduler); RxJavaPlugins.setComputationSchedulerHandler(scheduler2scheduler); RxJavaPlugins.setIoSchedulerHandler(scheduler2scheduler); RxJavaPlugins.setNewThreadSchedulerHandler(scheduler2scheduler); RxJavaPlugins.setOnConnectableFlowableAssembly(connectableFlowable2ConnectableFlowable); RxJavaPlugins.setOnConnectableObservableAssembly(connectableObservable2ConnectableObservable); RxJavaPlugins.setOnFlowableAssembly(flowable2Flowable); RxJavaPlugins.setOnFlowableSubscribe(flowable2subscriber); RxJavaPlugins.setOnMaybeAssembly(maybe2maybe); RxJavaPlugins.setOnMaybeSubscribe(maybe2observer); RxJavaPlugins.setOnObservableAssembly(observable2observable); RxJavaPlugins.setOnObservableSubscribe(observable2observer); RxJavaPlugins.setOnParallelAssembly(parallelFlowable2parallelFlowable); RxJavaPlugins.setOnSingleAssembly(single2single); RxJavaPlugins.setOnSingleSubscribe(single2observer); RxJavaPlugins.setScheduleHandler(runnable2runnable); RxJavaPlugins.setSingleSchedulerHandler(scheduler2scheduler); RxJavaPlugins.setOnCompletableSubscribe(completableObserver2completableObserver); RxJavaPlugins.setOnCompletableAssembly(completable2completable); RxJavaPlugins.setInitSingleSchedulerHandler(callable2scheduler); RxJavaPlugins.setInitNewThreadSchedulerHandler(callable2scheduler); RxJavaPlugins.setInitIoSchedulerHandler(callable2scheduler); } finally { RxJavaPlugins.reset(); } } @SuppressWarnings({"rawtypes", "unchecked" }) @Test public void clearIsPassthrough() { try { RxJavaPlugins.reset(); assertNull(RxJavaPlugins.onAssembly((Observable)null)); assertNull(RxJavaPlugins.onAssembly((ConnectableObservable)null)); assertNull(RxJavaPlugins.onAssembly((Flowable)null)); assertNull(RxJavaPlugins.onAssembly((ConnectableFlowable)null)); Observable oos = new Observable() { @Override public void subscribeActual(Observer t) { } }; Flowable fos = new Flowable() { @Override public void subscribeActual(Subscriber t) { } }; assertSame(oos, RxJavaPlugins.onAssembly(oos)); assertSame(fos, RxJavaPlugins.onAssembly(fos)); assertNull(RxJavaPlugins.onAssembly((Single)null)); Single sos = new Single() { @Override public void subscribeActual(SingleObserver t) { } }; assertSame(sos, RxJavaPlugins.onAssembly(sos)); assertNull(RxJavaPlugins.onAssembly((Completable)null)); Completable cos = new Completable() { @Override public void subscribeActual(CompletableObserver t) { } }; assertSame(cos, RxJavaPlugins.onAssembly(cos)); assertNull(RxJavaPlugins.onAssembly((Maybe)null)); assertNull(RxJavaPlugins.onSchedule(null)); Maybe myb = new Maybe() { @Override public void subscribeActual(MaybeObserver t) { } }; assertSame(myb, RxJavaPlugins.onAssembly(myb)); assertNull(RxJavaPlugins.onSchedule(null)); Runnable action = Functions.EMPTY_RUNNABLE; assertSame(action, RxJavaPlugins.onSchedule(action)); class AllSubscriber implements Subscriber, Observer, SingleObserver, CompletableObserver, MaybeObserver { @Override public void onSuccess(Object value) { } @Override public void onSubscribe(Disposable d) { } @Override public void onSubscribe(Subscription s) { } @Override public void onNext(Object t) { } @Override public void onError(Throwable t) { } @Override public void onComplete() { } } AllSubscriber all = new AllSubscriber(); assertNull(RxJavaPlugins.onSubscribe(Observable.never(), null)); assertSame(all, RxJavaPlugins.onSubscribe(Observable.never(), all)); assertNull(RxJavaPlugins.onSubscribe(Flowable.never(), null)); assertSame(all, RxJavaPlugins.onSubscribe(Flowable.never(), all)); assertNull(RxJavaPlugins.onSubscribe(Single.just(1), null)); assertSame(all, RxJavaPlugins.onSubscribe(Single.just(1), all)); assertNull(RxJavaPlugins.onSubscribe(Completable.never(), null)); assertSame(all, RxJavaPlugins.onSubscribe(Completable.never(), all)); assertNull(RxJavaPlugins.onSubscribe(Maybe.never(), null)); assertSame(all, RxJavaPlugins.onSubscribe(Maybe.never(), all)); // These hooks don't exist in 2.0 // Subscription subscription = Subscriptions.empty(); // // assertNull(RxJavaPlugins.onObservableReturn(null)); // // assertSame(subscription, RxJavaPlugins.onObservableReturn(subscription)); // // assertNull(RxJavaPlugins.onSingleReturn(null)); // // assertSame(subscription, RxJavaPlugins.onSingleReturn(subscription)); // // TestException ex = new TestException(); // // assertNull(RxJavaPlugins.onObservableError(null)); // // assertSame(ex, RxJavaPlugins.onObservableError(ex)); // // assertNull(RxJavaPlugins.onSingleError(null)); // // assertSame(ex, RxJavaPlugins.onSingleError(ex)); // // assertNull(RxJavaPlugins.onCompletableError(null)); // // assertSame(ex, RxJavaPlugins.onCompletableError(ex)); // // Observable.Operator oop = new Observable.Operator() { // @Override // public Object call(Object t) { // return t; // } // }; // // assertNull(RxJavaPlugins.onObservableLift(null)); // // assertSame(oop, RxJavaPlugins.onObservableLift(oop)); // // assertNull(RxJavaPlugins.onSingleLift(null)); // // assertSame(oop, RxJavaPlugins.onSingleLift(oop)); // // Completable.CompletableOperator cop = new Completable.CompletableOperator() { // @Override // public CompletableSubscriber call(CompletableSubscriber t) { // return t; // } // }; // // assertNull(RxJavaPlugins.onCompletableLift(null)); // // assertSame(cop, RxJavaPlugins.onCompletableLift(cop)); final Scheduler s = ImmediateThinScheduler.INSTANCE; Callable<Scheduler> c = new Callable<Scheduler>() { @Override public Scheduler call() throws Exception { return s; } }; assertSame(s, RxJavaPlugins.onComputationScheduler(s)); assertSame(s, RxJavaPlugins.onIoScheduler(s)); assertSame(s, RxJavaPlugins.onNewThreadScheduler(s)); assertSame(s, RxJavaPlugins.onSingleScheduler(s)); assertSame(s, RxJavaPlugins.initComputationScheduler(c)); assertSame(s, RxJavaPlugins.initIoScheduler(c)); assertSame(s, RxJavaPlugins.initNewThreadScheduler(c)); assertSame(s, RxJavaPlugins.initSingleScheduler(c)); } finally { RxJavaPlugins.reset(); } } static void assertTestException(List<Throwable> list, int index, String message) { assertTrue(list.get(index).toString(), list.get(index) instanceof TestException); assertEquals(message, list.get(index).getMessage()); } static void assertUndeliverableTestException(List<Throwable> list, int index, String message) { assertTrue(list.get(index).toString(), list.get(index).getCause() instanceof TestException); assertEquals(message, list.get(index).getCause().getMessage()); } static void assertNPE(List<Throwable> list, int index) { assertTrue(list.get(index).toString(), list.get(index) instanceof NullPointerException); } @Test @Ignore("Not present in 2.0") public void onXError() { // try { // final List<Throwable> list = new ArrayList<Throwable>(); // // final TestException ex = new TestException(); // // Function<Throwable, Throwable> errorHandler = new Function<Throwable, Throwable>() { // @Override // public Throwable a(Throwable t) { // list.add(t); // return ex; // } // }; // // RxJavaPlugins.setOnObservableSubscribeError(errorHandler); // // RxJavaPlugins.setOnSingleSubscribeError(errorHandler); // // RxJavaPlugins.setOnCompletableSubscribeError(errorHandler); // // assertSame(ex, RxJavaPlugins.onObservableError(new TestException("Forced failure 1"))); // // assertSame(ex, RxJavaPlugins.onSingleError(new TestException("Forced failure 2"))); // // assertSame(ex, RxJavaPlugins.onCompletableError(new TestException("Forced failure 3"))); // // assertTestException(list, 0, "Forced failure 1"); // // assertTestException(list, 1, "Forced failure 2"); // // assertTestException(list, 2, "Forced failure 3"); // } finally { // RxJavaPlugins.reset(); // } } // @SuppressWarnings("deprecation") @Test @Ignore("Not present in 2.0") public void onPluginsXError() { // try { // RxJavaPlugins.reset(); // // final List<Throwable> list = new ArrayList<Throwable>(); // // final TestException ex = new TestException(); // // final Function<Throwable, Throwable> errorHandler = new Function<Throwable, Throwable>() { // @Override // public Throwable apply(Throwable t) { // list.add(t); // return ex; // } // }; // // RxJavaPlugins.getInstance().registerObservableExecutionHook(new RxJavaObservableExecutionHook() { // @Override // public <T> Throwable onSubscribeError(Throwable e) { // return errorHandler.call(e); // } // }); // // RxJavaPlugins.getInstance().registerSingleExecutionHook(new RxJavaSingleExecutionHook() { // @Override // public <T> Throwable onSubscribeError(Throwable e) { // return errorHandler.call(e); // } // }); // // RxJavaPlugins.getInstance().registerCompletableExecutionHook(new RxJavaCompletableExecutionHook() { // @Override // public Throwable onSubscribeError(Throwable e) { // return errorHandler.call(e); // } // }); // // assertSame(ex, RxJavaPlugins.onObservableError(new TestException("Forced failure 1"))); // // assertSame(ex, RxJavaPlugins.onSingleError(new TestException("Forced failure 2"))); // // assertSame(ex, RxJavaPlugins.onCompletableError(new TestException("Forced failure 3"))); // // assertTestException(list, 0, "Forced failure 1"); // // assertTestException(list, 1, "Forced failure 2"); // // assertTestException(list, 2, "Forced failure 3"); // } finally { // RxJavaPlugins.getInstance().reset(); // RxJavaPlugins.reset(); // } } // @SuppressWarnings({ "rawtypes", "unchecked" }) @Test @Ignore("Not present in 2.0") public void onXLift() { // try { // Completable.CompletableOperator cop = new Completable.CompletableOperator() { // @Override // public CompletableSubscriber call(CompletableSubscriber t) { // return t; // } // }; // // Observable.Operator oop = new Observable.Operator() { // @Override // public Object call(Object t) { // return t; // } // }; // // final int[] counter = { 0 }; // // RxJavaPlugins.setOnObservableLift(new Function<Operator, Operator>() { // @Override // public Operator call(Operator t) { // counter[0]++; // return t; // } // }); // // RxJavaPlugins.setOnSingleLift(new Function<Operator, Operator>() { // @Override // public Operator call(Operator t) { // counter[0]++; // return t; // } // }); // // RxJavaPlugins.setOnCompletableLift(new Function<CompletableOperator, CompletableOperator>() { // @Override // public CompletableOperator call(CompletableOperator t) { // counter[0]++; // return t; // } // }); // // assertSame(oop, RxJavaPlugins.onObservableLift(oop)); // // assertSame(oop, RxJavaPlugins.onSingleLift(oop)); // // assertSame(cop, RxJavaPlugins.onCompletableLift(cop)); // // assertEquals(3, counter[0]); // // } finally { // RxJavaPlugins.reset(); // } } // @SuppressWarnings({ "rawtypes", "unchecked", "deprecation" }) @Test @Ignore("Not present in 2.0") public void onPluginsXLift() { // try { // // RxJavaPlugins.getInstance().reset(); // RxJavaPlugins.reset(); // // Completable.CompletableOperator cop = new Completable.CompletableOperator() { // @Override // public CompletableSubscriber call(CompletableSubscriber t) { // return t; // } // }; // // Observable.Operator oop = new Observable.Operator() { // @Override // public Object call(Object t) { // return t; // } // }; // // final int[] counter = { 0 }; // // final Function<Operator, Operator> onObservableLift = new Function<Operator, Operator>() { // @Override // public Operator call(Operator t) { // counter[0]++; // return t; // } // }; // // final Function<CompletableOperator, CompletableOperator> onCompletableLift = new Function<CompletableOperator, CompletableOperator>() { // @Override // public CompletableOperator call(CompletableOperator t) { // counter[0]++; // return t; // } // }; // // RxJavaPlugins.getInstance().registerObservableExecutionHook(new RxJavaObservableExecutionHook() { // @Override // public <T, R> Operator<? extends R, ? super T> onLift(Operator<? extends R, ? super T> lift) { // return onObservableLift.call(lift); // } // }); // // RxJavaPlugins.getInstance().registerSingleExecutionHook(new RxJavaSingleExecutionHook() { // @Override // public <T, R> Operator<? extends R, ? super T> onLift(Operator<? extends R, ? super T> lift) { // return onObservableLift.call(lift); // } // }); // // RxJavaPlugins.getInstance().registerCompletableExecutionHook(new RxJavaCompletableExecutionHook() { // @Override // public CompletableOperator onLift(CompletableOperator lift) { // return onCompletableLift.call(lift); // } // }); // // assertSame(oop, RxJavaPlugins.onObservableLift(oop)); // // assertSame(oop, RxJavaPlugins.onSingleLift(oop)); // // assertSame(cop, RxJavaPlugins.onCompletableLift(cop)); // // assertEquals(3, counter[0]); // // } finally { // RxJavaPlugins.reset(); // } } @SuppressWarnings("rawtypes") @Test public void overrideConnectableObservable() { try { RxJavaPlugins.setOnConnectableObservableAssembly(new Function<ConnectableObservable, ConnectableObservable>() { @Override public ConnectableObservable apply(ConnectableObservable co) throws Exception { return new ConnectableObservable() { @Override public void connect(Consumer connection) { } @SuppressWarnings("unchecked") @Override protected void subscribeActual(Observer observer) { observer.onSubscribe(Disposables.empty()); observer.onNext(10); observer.onComplete(); } }; } }); Observable .just(1) .publish() .autoConnect() .test() .assertResult(10); } finally { RxJavaPlugins.reset(); } Observable .just(1) .publish() .autoConnect() .test() .assertResult(1); } @SuppressWarnings("rawtypes") @Test public void overrideConnectableFlowable() { try { RxJavaPlugins.setOnConnectableFlowableAssembly(new Function<ConnectableFlowable, ConnectableFlowable>() { @Override public ConnectableFlowable apply(ConnectableFlowable co) throws Exception { return new ConnectableFlowable() { @Override public void connect(Consumer connection) { } @SuppressWarnings("unchecked") @Override protected void subscribeActual(Subscriber subscriber) { subscriber.onSubscribe(new ScalarSubscription(subscriber, 10)); } }; } }); Flowable .just(1) .publish() .autoConnect() .test() .assertResult(10); } finally { RxJavaPlugins.reset(); } Flowable .just(1) .publish() .autoConnect() .test() .assertResult(1); } @SuppressWarnings("rawtypes") @Test public void assemblyHookCrashes() { try { RxJavaPlugins.setOnFlowableAssembly(new Function<Flowable, Flowable>() { @Override public Flowable apply(Flowable f) throws Exception { throw new IllegalArgumentException(); } }); try { Flowable.empty(); fail("Should have thrown!"); } catch (IllegalArgumentException ex) { // expected } RxJavaPlugins.setOnFlowableAssembly(new Function<Flowable, Flowable>() { @Override public Flowable apply(Flowable f) throws Exception { throw new InternalError(); } }); try { Flowable.empty(); fail("Should have thrown!"); } catch (InternalError ex) { // expected } RxJavaPlugins.setOnFlowableAssembly(new Function<Flowable, Flowable>() { @Override public Flowable apply(Flowable f) throws Exception { throw new IOException(); } }); try { Flowable.empty(); fail("Should have thrown!"); } catch (RuntimeException ex) { if (!(ex.getCause() instanceof IOException)) { fail(ex.getCause().toString() + ": Should have thrown RuntimeException(IOException)"); } } } finally { RxJavaPlugins.reset(); } } @SuppressWarnings("rawtypes") @Test public void subscribeHookCrashes() { try { RxJavaPlugins.setOnFlowableSubscribe(new BiFunction<Flowable, Subscriber, Subscriber>() { @Override public Subscriber apply(Flowable f, Subscriber s) throws Exception { throw new IllegalArgumentException(); } }); try { Flowable.empty().test(); fail("Should have thrown!"); } catch (NullPointerException ex) { if (!(ex.getCause() instanceof IllegalArgumentException)) { fail(ex.getCause().toString() + ": Should have thrown NullPointerException(IllegalArgumentException)"); } } RxJavaPlugins.setOnFlowableSubscribe(new BiFunction<Flowable, Subscriber, Subscriber>() { @Override public Subscriber apply(Flowable f, Subscriber s) throws Exception { throw new InternalError(); } }); try { Flowable.empty().test(); fail("Should have thrown!"); } catch (InternalError ex) { // expected } RxJavaPlugins.setOnFlowableSubscribe(new BiFunction<Flowable, Subscriber, Subscriber>() { @Override public Subscriber apply(Flowable f, Subscriber s) throws Exception { throw new IOException(); } }); try { Flowable.empty().test(); fail("Should have thrown!"); } catch (NullPointerException ex) { if (!(ex.getCause() instanceof RuntimeException)) { fail(ex.getCause().toString() + ": Should have thrown NullPointerException(RuntimeException(IOException))"); } if (!(ex.getCause().getCause() instanceof IOException)) { fail(ex.getCause().toString() + ": Should have thrown NullPointerException(RuntimeException(IOException))"); } } } finally { RxJavaPlugins.reset(); } } @SuppressWarnings("rawtypes") @Test public void maybeCreate() { try { RxJavaPlugins.setOnMaybeAssembly(new Function<Maybe, Maybe>() { @Override public Maybe apply(Maybe t) { return new MaybeError(new TestException()); } }); Maybe.empty() .test() .assertNoValues() .assertNotComplete() .assertError(TestException.class); } finally { RxJavaPlugins.reset(); } // make sure the reset worked Maybe.empty() .test() .assertNoValues() .assertNoErrors() .assertComplete(); } @Test @SuppressWarnings("rawtypes") public void maybeStart() { try { RxJavaPlugins.setOnMaybeSubscribe(new BiFunction<Maybe, MaybeObserver, MaybeObserver>() { @Override public MaybeObserver apply(Maybe o, final MaybeObserver t) { return new MaybeObserver() { @Override public void onSubscribe(Disposable d) { t.onSubscribe(d); } @SuppressWarnings("unchecked") @Override public void onSuccess(Object value) { t.onSuccess(value); } @Override public void onError(Throwable e) { t.onError(e); } @Override public void onComplete() { t.onError(new TestException()); } }; } }); Maybe.empty() .test() .assertNoValues() .assertNotComplete() .assertError(TestException.class); } finally { RxJavaPlugins.reset(); } // make sure the reset worked Maybe.empty() .test() .assertNoValues() .assertNoErrors() .assertComplete(); } @Test public void onErrorNull() { try { final AtomicReference<Throwable> t = new AtomicReference<Throwable>(); RxJavaPlugins.setErrorHandler(new Consumer<Throwable>() { @Override public void accept(final Throwable throwable) throws Exception { t.set(throwable); } }); RxJavaPlugins.onError(null); final Throwable throwable = t.get(); assertEquals("onError called with null. Null values are generally not allowed in 2.x operators and sources.", throwable.getMessage()); assertTrue(throwable instanceof NullPointerException); } finally { RxJavaPlugins.reset(); } } private static void verifyThread(Scheduler scheduler, String expectedThreadName) throws AssertionError { assertNotNull(scheduler); Worker w = scheduler.createWorker(); try { final AtomicReference<Thread> value = new AtomicReference<Thread>(); final CountDownLatch cdl = new CountDownLatch(1); w.schedule(new Runnable() { @Override public void run() { value.set(Thread.currentThread()); cdl.countDown(); } }); cdl.await(); Thread t = value.get(); assertNotNull(t); assertTrue(expectedThreadName.equals(t.getName())); } catch (Exception e) { fail(); } finally { w.dispose(); } } @Test public void createComputationScheduler() { final String name = "ComputationSchedulerTest"; ThreadFactory factory = new ThreadFactory() { @Override public Thread newThread(Runnable r) { return new Thread(r, name); } }; final Scheduler customScheduler = RxJavaPlugins.createComputationScheduler(factory); RxJavaPlugins.setComputationSchedulerHandler(new Function<Scheduler, Scheduler>() { @Override public Scheduler apply(Scheduler scheduler) throws Exception { return customScheduler; } }); try { verifyThread(Schedulers.computation(), name); } finally { customScheduler.shutdown(); RxJavaPlugins.reset(); } } @Test public void createIoScheduler() { final String name = "IoSchedulerTest"; ThreadFactory factory = new ThreadFactory() { @Override public Thread newThread(Runnable r) { return new Thread(r, name); } }; final Scheduler customScheduler = RxJavaPlugins.createIoScheduler(factory); RxJavaPlugins.setIoSchedulerHandler(new Function<Scheduler, Scheduler>() { @Override public Scheduler apply(Scheduler scheduler) throws Exception { return customScheduler; } }); try { verifyThread(Schedulers.io(), name); } finally { customScheduler.shutdown(); RxJavaPlugins.reset(); } } @Test public void createNewThreadScheduler() { final String name = "NewThreadSchedulerTest"; ThreadFactory factory = new ThreadFactory() { @Override public Thread newThread(Runnable r) { return new Thread(r, name); } }; final Scheduler customScheduler = RxJavaPlugins.createNewThreadScheduler(factory); RxJavaPlugins.setNewThreadSchedulerHandler(new Function<Scheduler, Scheduler>() { @Override public Scheduler apply(Scheduler scheduler) throws Exception { return customScheduler; } }); try { verifyThread(Schedulers.newThread(), name); } finally { customScheduler.shutdown(); RxJavaPlugins.reset(); } } @Test public void createSingleScheduler() { final String name = "SingleSchedulerTest"; ThreadFactory factory = new ThreadFactory() { @Override public Thread newThread(Runnable r) { return new Thread(r, name); } }; final Scheduler customScheduler = RxJavaPlugins.createSingleScheduler(factory); RxJavaPlugins.setSingleSchedulerHandler(new Function<Scheduler, Scheduler>() { @Override public Scheduler apply(Scheduler scheduler) throws Exception { return customScheduler; } }); try { verifyThread(Schedulers.single(), name); } finally { customScheduler.shutdown(); RxJavaPlugins.reset(); } } @Test public void onBeforeBlocking() { try { RxJavaPlugins.setOnBeforeBlocking(new BooleanSupplier() { @Override public boolean getAsBoolean() throws Exception { throw new IllegalArgumentException(); } }); try { RxJavaPlugins.onBeforeBlocking(); fail("Should have thrown"); } catch (IllegalArgumentException ex) { // expected } } finally { RxJavaPlugins.reset(); } } @SuppressWarnings("rawtypes") @Test public void onParallelAssembly() { try { RxJavaPlugins.setOnParallelAssembly(new Function<ParallelFlowable, ParallelFlowable>() { @Override public ParallelFlowable apply(ParallelFlowable pf) throws Exception { return new ParallelFromPublisher<Integer>(Flowable.just(2), 2, 2); } }); Flowable.just(1) .parallel() .sequential() .test() .assertResult(2); } finally { RxJavaPlugins.reset(); } Flowable.just(1) .parallel() .sequential() .test() .assertResult(1); } @Test public void isBug() { assertFalse(RxJavaPlugins.isBug(new RuntimeException())); assertFalse(RxJavaPlugins.isBug(new IOException())); assertFalse(RxJavaPlugins.isBug(new InterruptedException())); assertFalse(RxJavaPlugins.isBug(new InterruptedIOException())); assertTrue(RxJavaPlugins.isBug(new NullPointerException())); assertTrue(RxJavaPlugins.isBug(new IllegalArgumentException())); assertTrue(RxJavaPlugins.isBug(new IllegalStateException())); assertTrue(RxJavaPlugins.isBug(new MissingBackpressureException())); assertTrue(RxJavaPlugins.isBug(new ProtocolViolationException(""))); assertTrue(RxJavaPlugins.isBug(new UndeliverableException(new TestException()))); assertTrue(RxJavaPlugins.isBug(new CompositeException(new TestException()))); assertTrue(RxJavaPlugins.isBug(new OnErrorNotImplementedException(new TestException()))); } }