/** * 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.completable; import static org.junit.Assert.*; import static org.mockito.Mockito.*; 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.disposables.Disposable; import io.reactivex.exceptions.*; import io.reactivex.functions.*; import io.reactivex.internal.disposables.*; import io.reactivex.internal.functions.Functions; import io.reactivex.internal.subscriptions.BooleanSubscription; import io.reactivex.internal.util.ExceptionHelper; import io.reactivex.observers.TestObserver; import io.reactivex.plugins.RxJavaPlugins; import io.reactivex.processors.PublishProcessor; import io.reactivex.schedulers.*; import io.reactivex.subjects.PublishSubject; import io.reactivex.subscribers.TestSubscriber; /** * Test Completable methods and operators. */ public class CompletableTest { /** * Iterable that returns an Iterator that throws in its hasNext method. */ static final class IterableIteratorNextThrows implements Iterable<Completable> { @Override public Iterator<Completable> iterator() { return new Iterator<Completable>() { @Override public boolean hasNext() { return true; } @Override public Completable next() { throw new TestException(); } @Override public void remove() { // TODO Auto-generated method stub } }; } } /** * Iterable that returns an Iterator that throws in its next method. */ static final class IterableIteratorHasNextThrows implements Iterable<Completable> { @Override public Iterator<Completable> iterator() { return new Iterator<Completable>() { @Override public boolean hasNext() { throw new TestException(); } @Override public Completable next() { return null; } @Override public void remove() { // TODO Auto-generated method stub } }; } } /** * A class containing a completable instance and counts the number of subscribers. */ static final class NormalCompletable extends AtomicInteger { private static final long serialVersionUID = 7192337844700923752L; public final Completable completable = Completable.unsafeCreate(new CompletableSource() { @Override public void subscribe(CompletableObserver s) { getAndIncrement(); EmptyDisposable.complete(s); } }); /** * Asserts the given number of subscriptions happened. * @param n the expected number of subscriptions */ public void assertSubscriptions(int n) { Assert.assertEquals(n, get()); } } /** * A class containing a completable instance that emits a TestException and counts * the number of subscribers. */ static final class ErrorCompletable extends AtomicInteger { private static final long serialVersionUID = 7192337844700923752L; public final Completable completable = Completable.unsafeCreate(new CompletableSource() { @Override public void subscribe(CompletableObserver s) { getAndIncrement(); EmptyDisposable.error(new TestException(), s); } }); /** * Asserts the given number of subscriptions happened. * @param n the expected number of subscriptions */ public void assertSubscriptions(int n) { Assert.assertEquals(n, get()); } } /** A normal Completable object. */ final NormalCompletable normal = new NormalCompletable(); /** An error Completable object. */ final ErrorCompletable error = new ErrorCompletable(); @Test(timeout = 5000) public void complete() { Completable c = Completable.complete(); c.blockingAwait(); } @Test(expected = NullPointerException.class) public void concatNull() { Completable.concatArray((Completable[])null); } @Test(timeout = 5000) public void concatEmpty() { Completable c = Completable.concatArray(); c.blockingAwait(); } @Test(timeout = 5000) public void concatSingleSource() { Completable c = Completable.concatArray(normal.completable); c.blockingAwait(); normal.assertSubscriptions(1); } @Test(timeout = 5000, expected = TestException.class) public void concatSingleSourceThrows() { Completable c = Completable.concatArray(error.completable); c.blockingAwait(); } @Test(timeout = 5000) public void concatMultipleSources() { Completable c = Completable.concatArray(normal.completable, normal.completable, normal.completable); c.blockingAwait(); normal.assertSubscriptions(3); } @Test(timeout = 5000, expected = TestException.class) public void concatMultipleOneThrows() { Completable c = Completable.concatArray(normal.completable, error.completable, normal.completable); c.blockingAwait(); } @Test(timeout = 5000, expected = NullPointerException.class) public void concatMultipleOneIsNull() { Completable c = Completable.concatArray(normal.completable, null); c.blockingAwait(); } @Test(timeout = 5000) public void concatIterableEmpty() { Completable c = Completable.concat(Collections.<Completable>emptyList()); c.blockingAwait(); } @Test(expected = NullPointerException.class) public void concatIterableNull() { Completable.concat((Iterable<Completable>)null); } @Test(timeout = 5000, expected = NullPointerException.class) public void concatIterableIteratorNull() { Completable c = Completable.concat(new Iterable<Completable>() { @Override public Iterator<Completable> iterator() { return null; } }); c.blockingAwait(); } @Test(timeout = 5000, expected = NullPointerException.class) public void concatIterableWithNull() { Completable c = Completable.concat(Arrays.asList(normal.completable, (Completable)null)); c.blockingAwait(); } @Test(timeout = 5000) public void concatIterableSingle() { Completable c = Completable.concat(Collections.singleton(normal.completable)); c.blockingAwait(); normal.assertSubscriptions(1); } @Test(timeout = 5000) public void concatIterableMany() { Completable c = Completable.concat(Arrays.asList(normal.completable, normal.completable, normal.completable)); c.blockingAwait(); normal.assertSubscriptions(3); } @Test(timeout = 5000, expected = TestException.class) public void concatIterableOneThrows() { Completable c = Completable.concat(Collections.singleton(error.completable)); c.blockingAwait(); } @Test(timeout = 5000, expected = TestException.class) public void concatIterableManyOneThrows() { Completable c = Completable.concat(Arrays.asList(normal.completable, error.completable)); c.blockingAwait(); } @Test(expected = TestException.class) public void concatIterableIterableThrows() { Completable c = Completable.concat(new Iterable<Completable>() { @Override public Iterator<Completable> iterator() { throw new TestException(); } }); c.blockingAwait(); } @Test(expected = TestException.class) public void concatIterableIteratorHasNextThrows() { Completable c = Completable.concat(new IterableIteratorHasNextThrows()); c.blockingAwait(); } @Test(expected = TestException.class) public void concatIterableIteratorNextThrows() { Completable c = Completable.concat(new IterableIteratorNextThrows()); c.blockingAwait(); } @Test(timeout = 5000) public void concatObservableEmpty() { Completable c = Completable.concat(Flowable.<Completable>empty()); c.blockingAwait(); } @Test(timeout = 5000, expected = TestException.class) public void concatObservableError() { Completable c = Completable.concat(Flowable.<Completable>error(new Callable<Throwable>() { @Override public Throwable call() { return new TestException(); } })); c.blockingAwait(); } @Test(timeout = 5000) public void concatObservableSingle() { Completable c = Completable.concat(Flowable.just(normal.completable)); c.blockingAwait(); normal.assertSubscriptions(1); } @Test(timeout = 5000, expected = TestException.class) public void concatObservableSingleThrows() { Completable c = Completable.concat(Flowable.just(error.completable)); c.blockingAwait(); } @Test(timeout = 5000) public void concatObservableMany() { Completable c = Completable.concat(Flowable.just(normal.completable).repeat(3)); c.blockingAwait(); normal.assertSubscriptions(3); } @Test(timeout = 5000, expected = TestException.class) public void concatObservableManyOneThrows() { Completable c = Completable.concat(Flowable.just(normal.completable, error.completable)); c.blockingAwait(); } @Test(timeout = 5000) public void concatObservablePrefetch() { final List<Long> requested = new ArrayList<Long>(); Flowable<Completable> cs = Flowable .just(normal.completable) .repeat(10) .doOnRequest(new LongConsumer() { @Override public void accept(long v) { requested.add(v); } }); Completable c = Completable.concat(cs, 5); c.blockingAwait(); Assert.assertEquals(Arrays.asList(5L, 4L, 4L), requested); } @Test(expected = NullPointerException.class) public void createNull() { Completable.unsafeCreate(null); } @Test(timeout = 5000, expected = NullPointerException.class) public void createOnSubscribeThrowsNPE() { Completable c = Completable.unsafeCreate(new CompletableSource() { @Override public void subscribe(CompletableObserver s) { throw new NullPointerException(); } }); c.blockingAwait(); } @Test(timeout = 5000) public void createOnSubscribeThrowsRuntimeException() { try { Completable c = Completable.unsafeCreate(new CompletableSource() { @Override public void subscribe(CompletableObserver s) { throw new TestException(); } }); c.blockingAwait(); Assert.fail("Did not throw exception"); } catch (NullPointerException ex) { if (!(ex.getCause() instanceof TestException)) { ex.printStackTrace(); Assert.fail("Did not wrap the TestException but it returned: " + ex); } } } @Test(timeout = 5000) public void defer() { Completable c = Completable.defer(new Callable<Completable>() { @Override public Completable call() { return normal.completable; } }); normal.assertSubscriptions(0); c.blockingAwait(); normal.assertSubscriptions(1); } @Test(expected = NullPointerException.class) public void deferNull() { Completable.defer(null); } @Test(timeout = 5000, expected = NullPointerException.class) public void deferReturnsNull() { Completable c = Completable.defer(new Callable<Completable>() { @Override public Completable call() { return null; } }); c.blockingAwait(); } @Test(timeout = 5000, expected = TestException.class) public void deferFunctionThrows() { Completable c = Completable.defer(new Callable<Completable>() { @Override public Completable call() { throw new TestException(); } }); c.blockingAwait(); } @Test(timeout = 5000, expected = TestException.class) public void deferErrorSource() { Completable c = Completable.defer(new Callable<Completable>() { @Override public Completable call() { return error.completable; } }); c.blockingAwait(); } @Test(expected = NullPointerException.class) public void errorSupplierNull() { Completable.error((Callable<Throwable>)null); } @Test(timeout = 5000, expected = TestException.class) public void errorSupplierNormal() { Completable c = Completable.error(new Callable<Throwable>() { @Override public Throwable call() { return new TestException(); } }); c.blockingAwait(); } @Test(timeout = 5000, expected = NullPointerException.class) public void errorSupplierReturnsNull() { Completable c = Completable.error(new Callable<Throwable>() { @Override public Throwable call() { return null; } }); c.blockingAwait(); } @Test(timeout = 5000, expected = TestException.class) public void errorSupplierThrows() { Completable c = Completable.error(new Callable<Throwable>() { @Override public Throwable call() { throw new TestException(); } }); c.blockingAwait(); } @Test(expected = NullPointerException.class) public void errorNull() { Completable.error((Throwable)null); } @Test(timeout = 5000, expected = TestException.class) public void errorNormal() { Completable c = Completable.error(new TestException()); c.blockingAwait(); } @Test(expected = NullPointerException.class) public void fromCallableNull() { Completable.fromCallable(null); } @Test(timeout = 5000) public void fromCallableNormal() { final AtomicInteger calls = new AtomicInteger(); Completable c = Completable.fromCallable(new Callable<Object>() { @Override public Object call() throws Exception { return calls.getAndIncrement(); } }); c.blockingAwait(); Assert.assertEquals(1, calls.get()); } @Test(timeout = 5000, expected = TestException.class) public void fromCallableThrows() { Completable c = Completable.fromCallable(new Callable<Object>() { @Override public Object call() throws Exception { throw new TestException(); } }); c.blockingAwait(); } @Test(expected = NullPointerException.class) public void fromFlowableNull() { Completable.fromPublisher(null); } @Test(timeout = 5000) public void fromFlowableEmpty() { Completable c = Completable.fromPublisher(Flowable.empty()); c.blockingAwait(); } @Test(timeout = 5000) public void fromFlowableSome() { for (int n = 1; n < 10000; n *= 10) { Completable c = Completable.fromPublisher(Flowable.range(1, n)); c.blockingAwait(); } } @Test(timeout = 5000, expected = TestException.class) public void fromFlowableError() { Completable c = Completable.fromPublisher(Flowable.error(new Callable<Throwable>() { @Override public Throwable call() { return new TestException(); } })); c.blockingAwait(); } @Test(expected = NullPointerException.class) public void fromObservableNull() { Completable.fromObservable(null); } @Test(timeout = 5000) public void fromObservableEmpty() { Completable c = Completable.fromObservable(Observable.empty()); c.blockingAwait(); } @Test(timeout = 5000) public void fromObservableSome() { for (int n = 1; n < 10000; n *= 10) { Completable c = Completable.fromObservable(Observable.range(1, n)); c.blockingAwait(); } } @Test(timeout = 5000, expected = TestException.class) public void fromObservableError() { Completable c = Completable.fromObservable(Observable.error(new Callable<Throwable>() { @Override public Throwable call() { return new TestException(); } })); c.blockingAwait(); } @Test(expected = NullPointerException.class) public void fromActionNull() { Completable.fromAction(null); } @Test(timeout = 5000) public void fromActionNormal() { final AtomicInteger calls = new AtomicInteger(); Completable c = Completable.fromAction(new Action() { @Override public void run() { calls.getAndIncrement(); } }); c.blockingAwait(); Assert.assertEquals(1, calls.get()); } @Test(timeout = 5000, expected = TestException.class) public void fromActionThrows() { Completable c = Completable.fromAction(new Action() { @Override public void run() { throw new TestException(); } }); c.blockingAwait(); } @Test(expected = NullPointerException.class) public void fromSingleNull() { Completable.fromSingle(null); } @Test(timeout = 5000) public void fromSingleNormal() { Completable c = Completable.fromSingle(Single.just(1)); c.blockingAwait(); } @Test(timeout = 5000, expected = TestException.class) public void fromSingleThrows() { Completable c = Completable.fromSingle(Single.error(new Callable<Throwable>() { @Override public Throwable call() { return new TestException(); } })); c.blockingAwait(); } @Test(expected = NullPointerException.class) public void mergeNull() { Completable.mergeArray((Completable[])null); } @Test(timeout = 5000) public void mergeEmpty() { Completable c = Completable.mergeArray(); c.blockingAwait(); } @Test(timeout = 5000) public void mergeSingleSource() { Completable c = Completable.mergeArray(normal.completable); c.blockingAwait(); normal.assertSubscriptions(1); } @Test(timeout = 5000, expected = TestException.class) public void mergeSingleSourceThrows() { Completable c = Completable.mergeArray(error.completable); c.blockingAwait(); } @Test(timeout = 5000) public void mergeMultipleSources() { Completable c = Completable.mergeArray(normal.completable, normal.completable, normal.completable); c.blockingAwait(); normal.assertSubscriptions(3); } @Test(timeout = 5000, expected = TestException.class) public void mergeMultipleOneThrows() { Completable c = Completable.mergeArray(normal.completable, error.completable, normal.completable); c.blockingAwait(); } @Test(timeout = 5000, expected = NullPointerException.class) public void mergeMultipleOneIsNull() { Completable c = Completable.mergeArray(normal.completable, null); c.blockingAwait(); } @Test(timeout = 5000) public void mergeIterableEmpty() { Completable c = Completable.merge(Collections.<Completable>emptyList()); c.blockingAwait(); } @Test(expected = NullPointerException.class) public void mergeIterableNull() { Completable.merge((Iterable<Completable>)null); } @Test(timeout = 5000, expected = NullPointerException.class) public void mergeIterableIteratorNull() { Completable c = Completable.merge(new Iterable<Completable>() { @Override public Iterator<Completable> iterator() { return null; } }); c.blockingAwait(); } @Test(timeout = 5000, expected = NullPointerException.class) public void mergeIterableWithNull() { Completable c = Completable.merge(Arrays.asList(normal.completable, (Completable)null)); c.blockingAwait(); } @Test(timeout = 5000) public void mergeIterableSingle() { Completable c = Completable.merge(Collections.singleton(normal.completable)); c.blockingAwait(); normal.assertSubscriptions(1); } @Test(timeout = 5000) public void mergeIterableMany() { Completable c = Completable.merge(Arrays.asList(normal.completable, normal.completable, normal.completable)); c.blockingAwait(); normal.assertSubscriptions(3); } @Test(timeout = 5000, expected = TestException.class) public void mergeIterableOneThrows() { Completable c = Completable.merge(Collections.singleton(error.completable)); c.blockingAwait(); } @Test(timeout = 5000, expected = TestException.class) public void mergeIterableManyOneThrows() { Completable c = Completable.merge(Arrays.asList(normal.completable, error.completable)); c.blockingAwait(); } @Test(expected = TestException.class) public void mergeIterableIterableThrows() { Completable c = Completable.merge(new Iterable<Completable>() { @Override public Iterator<Completable> iterator() { throw new TestException(); } }); c.blockingAwait(); } @Test(expected = TestException.class) public void mergeIterableIteratorHasNextThrows() { Completable c = Completable.merge(new IterableIteratorHasNextThrows()); c.blockingAwait(); } @Test(expected = TestException.class) public void mergeIterableIteratorNextThrows() { Completable c = Completable.merge(new IterableIteratorNextThrows()); c.blockingAwait(); } @Test(timeout = 5000) public void mergeObservableEmpty() { Completable c = Completable.merge(Flowable.<Completable>empty()); c.blockingAwait(); } @Test(timeout = 5000, expected = TestException.class) public void mergeObservableError() { Completable c = Completable.merge(Flowable.<Completable>error(new Callable<Throwable>() { @Override public Throwable call() { return new TestException(); } })); c.blockingAwait(); } @Test(timeout = 5000) public void mergeObservableSingle() { Completable c = Completable.merge(Flowable.just(normal.completable)); c.blockingAwait(); normal.assertSubscriptions(1); } @Test(timeout = 5000, expected = TestException.class) public void mergeObservableSingleThrows() { Completable c = Completable.merge(Flowable.just(error.completable)); c.blockingAwait(); } @Test(timeout = 5000) public void mergeObservableMany() { Completable c = Completable.merge(Flowable.just(normal.completable).repeat(3)); c.blockingAwait(); normal.assertSubscriptions(3); } @Test(timeout = 5000, expected = TestException.class) public void mergeObservableManyOneThrows() { Completable c = Completable.merge(Flowable.just(normal.completable, error.completable)); c.blockingAwait(); } @Test(timeout = 5000) public void mergeObservableMaxConcurrent() { final List<Long> requested = new ArrayList<Long>(); Flowable<Completable> cs = Flowable .just(normal.completable) .repeat(10) .doOnRequest(new LongConsumer() { @Override public void accept(long v) { requested.add(v); } }); Completable c = Completable.merge(cs, 5); c.blockingAwait(); // FIXME this request pattern looks odd because all 10 completions trigger 1 requests Assert.assertEquals(Arrays.asList(5L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L), requested); } @Test(expected = NullPointerException.class) public void mergeDelayErrorNull() { Completable.mergeArrayDelayError((Completable[])null); } @Test(timeout = 5000) public void mergeDelayErrorEmpty() { Completable c = Completable.mergeArrayDelayError(); c.blockingAwait(); } @Test(timeout = 5000) public void mergeDelayErrorSingleSource() { Completable c = Completable.mergeArrayDelayError(normal.completable); c.blockingAwait(); normal.assertSubscriptions(1); } @Test(timeout = 5000, expected = TestException.class) public void mergeDelayErrorSingleSourceThrows() { Completable c = Completable.mergeArrayDelayError(error.completable); c.blockingAwait(); } @Test(timeout = 5000) public void mergeDelayErrorMultipleSources() { Completable c = Completable.mergeArrayDelayError(normal.completable, normal.completable, normal.completable); c.blockingAwait(); normal.assertSubscriptions(3); } @Test(timeout = 5000) public void mergeDelayErrorMultipleOneThrows() { Completable c = Completable.mergeArrayDelayError(normal.completable, error.completable, normal.completable); try { c.blockingAwait(); } catch (TestException ex) { normal.assertSubscriptions(2); } } @Test(timeout = 5000, expected = NullPointerException.class) public void mergeDelayErrorMultipleOneIsNull() { Completable c = Completable.mergeArrayDelayError(normal.completable, null); c.blockingAwait(); } @Test(timeout = 5000) public void mergeDelayErrorIterableEmpty() { Completable c = Completable.mergeDelayError(Collections.<Completable>emptyList()); c.blockingAwait(); } @Test(expected = NullPointerException.class) public void mergeDelayErrorIterableNull() { Completable.mergeDelayError((Iterable<Completable>)null); } @Test(timeout = 5000, expected = NullPointerException.class) public void mergeDelayErrorIterableIteratorNull() { Completable c = Completable.mergeDelayError(new Iterable<Completable>() { @Override public Iterator<Completable> iterator() { return null; } }); c.blockingAwait(); } @Test(timeout = 5000, expected = NullPointerException.class) public void mergeDelayErrorIterableWithNull() { Completable c = Completable.mergeDelayError(Arrays.asList(normal.completable, (Completable)null)); c.blockingAwait(); } @Test(timeout = 5000) public void mergeDelayErrorIterableSingle() { Completable c = Completable.mergeDelayError(Collections.singleton(normal.completable)); c.blockingAwait(); normal.assertSubscriptions(1); } @Test(timeout = 5000) public void mergeDelayErrorIterableMany() { Completable c = Completable.mergeDelayError(Arrays.asList(normal.completable, normal.completable, normal.completable)); c.blockingAwait(); normal.assertSubscriptions(3); } @Test(timeout = 5000, expected = TestException.class) public void mergeDelayErrorIterableOneThrows() { Completable c = Completable.mergeDelayError(Collections.singleton(error.completable)); c.blockingAwait(); } @Test(timeout = 5000) public void mergeDelayErrorIterableManyOneThrows() { Completable c = Completable.mergeDelayError(Arrays.asList(normal.completable, error.completable, normal.completable)); try { c.blockingAwait(); } catch (TestException ex) { normal.assertSubscriptions(2); } } @Test(expected = TestException.class) public void mergeDelayErrorIterableIterableThrows() { Completable c = Completable.mergeDelayError(new Iterable<Completable>() { @Override public Iterator<Completable> iterator() { throw new TestException(); } }); c.blockingAwait(); } @Test(expected = TestException.class) public void mergeDelayErrorIterableIteratorHasNextThrows() { Completable c = Completable.mergeDelayError(new IterableIteratorHasNextThrows()); c.blockingAwait(); } @Test(expected = TestException.class) public void mergeDelayErrorIterableIteratorNextThrows() { Completable c = Completable.mergeDelayError(new IterableIteratorNextThrows()); c.blockingAwait(); } @Test(timeout = 5000) public void mergeDelayErrorObservableEmpty() { Completable c = Completable.mergeDelayError(Flowable.<Completable>empty()); c.blockingAwait(); } @Test(timeout = 5000, expected = TestException.class) public void mergeDelayErrorObservableError() { Completable c = Completable.mergeDelayError(Flowable.<Completable>error(new Callable<Throwable>() { @Override public Throwable call() { return new TestException(); } })); c.blockingAwait(); } @Test(timeout = 5000) public void mergeDelayErrorObservableSingle() { Completable c = Completable.mergeDelayError(Flowable.just(normal.completable)); c.blockingAwait(); normal.assertSubscriptions(1); } @Test(timeout = 5000, expected = TestException.class) public void mergeDelayErrorObservableSingleThrows() { Completable c = Completable.mergeDelayError(Flowable.just(error.completable)); c.blockingAwait(); } @Test(timeout = 5000) public void mergeDelayErrorObservableMany() { Completable c = Completable.mergeDelayError(Flowable.just(normal.completable).repeat(3)); c.blockingAwait(); normal.assertSubscriptions(3); } @Test(timeout = 5000, expected = TestException.class) public void mergeDelayErrorObservableManyOneThrows() { Completable c = Completable.mergeDelayError(Flowable.just(normal.completable, error.completable)); c.blockingAwait(); } @Test(timeout = 5000) public void mergeDelayErrorObservableMaxConcurrent() { final List<Long> requested = new ArrayList<Long>(); Flowable<Completable> cs = Flowable .just(normal.completable) .repeat(10) .doOnRequest(new LongConsumer() { @Override public void accept(long v) { requested.add(v); } }); Completable c = Completable.mergeDelayError(cs, 5); c.blockingAwait(); // FIXME this request pattern looks odd because all 10 completions trigger 1 requests Assert.assertEquals(Arrays.asList(5L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L), requested); } @Test(timeout = 5000) public void never() { final AtomicBoolean onSubscribeCalled = new AtomicBoolean(); final AtomicInteger calls = new AtomicInteger(); Completable.never().subscribe(new CompletableObserver() { @Override public void onSubscribe(Disposable d) { onSubscribeCalled.set(true); } @Override public void onError(Throwable e) { calls.getAndIncrement(); } @Override public void onComplete() { calls.getAndIncrement(); } }); Assert.assertTrue("onSubscribe not called", onSubscribeCalled.get()); Assert.assertEquals("There were calls to onXXX methods", 0, calls.get()); } @Test(timeout = 1500) public void timer() { Completable c = Completable.timer(500, TimeUnit.MILLISECONDS); c.blockingAwait(); } @Test(timeout = 1500) public void timerNewThread() { Completable c = Completable.timer(500, TimeUnit.MILLISECONDS, Schedulers.newThread()); c.blockingAwait(); } @Test(timeout = 5000) public void timerTestScheduler() { TestScheduler scheduler = new TestScheduler(); Completable c = Completable.timer(250, TimeUnit.MILLISECONDS, scheduler); final AtomicInteger calls = new AtomicInteger(); c.subscribe(new CompletableObserver() { @Override public void onSubscribe(Disposable d) { } @Override public void onComplete() { calls.getAndIncrement(); } @Override public void onError(Throwable e) { RxJavaPlugins.onError(e); } }); scheduler.advanceTimeBy(100, TimeUnit.MILLISECONDS); Assert.assertEquals(0, calls.get()); scheduler.advanceTimeBy(200, TimeUnit.MILLISECONDS); Assert.assertEquals(1, calls.get()); } @Test(timeout = 2000) public void timerCancel() throws InterruptedException { Completable c = Completable.timer(250, TimeUnit.MILLISECONDS); final SequentialDisposable sd = new SequentialDisposable(); final AtomicInteger calls = new AtomicInteger(); c.subscribe(new CompletableObserver() { @Override public void onSubscribe(Disposable d) { sd.replace(d); } @Override public void onError(Throwable e) { calls.getAndIncrement(); } @Override public void onComplete() { calls.getAndIncrement(); } }); Thread.sleep(100); sd.dispose(); Thread.sleep(200); Assert.assertEquals(0, calls.get()); } @Test(expected = NullPointerException.class) public void timerUnitNull() { Completable.timer(1, null); } @Test(expected = NullPointerException.class) public void timerSchedulerNull() { Completable.timer(1, TimeUnit.SECONDS, null); } @Test(timeout = 5000) public void usingNormalEager() { final AtomicInteger dispose = new AtomicInteger(); Completable c = Completable.using(new Callable<Integer>() { @Override public Integer call() { return 1; } }, new Function<Object, Completable>() { @Override public Completable apply(Object v) { return normal.completable; } }, new Consumer<Integer>() { @Override public void accept(Integer d) { dispose.set(d); } }); final AtomicBoolean disposedFirst = new AtomicBoolean(); final AtomicReference<Throwable> error = new AtomicReference<Throwable>(); c.subscribe(new CompletableObserver() { @Override public void onSubscribe(Disposable d) { } @Override public void onError(Throwable e) { error.lazySet(e); } @Override public void onComplete() { disposedFirst.set(dispose.get() != 0); } }); Assert.assertEquals(1, dispose.get()); Assert.assertTrue("Not disposed first", disposedFirst.get()); Assert.assertNull(error.get()); } @Test(timeout = 5000) public void usingNormalLazy() { final AtomicInteger dispose = new AtomicInteger(); Completable c = Completable.using(new Callable<Integer>() { @Override public Integer call() { return 1; } }, new Function<Integer, Completable>() { @Override public Completable apply(Integer v) { return normal.completable; } }, new Consumer<Integer>() { @Override public void accept(Integer d) { dispose.set(d); } }, false); final AtomicBoolean disposedFirst = new AtomicBoolean(); final AtomicReference<Throwable> error = new AtomicReference<Throwable>(); c.subscribe(new CompletableObserver() { @Override public void onSubscribe(Disposable d) { } @Override public void onError(Throwable e) { error.lazySet(e); } @Override public void onComplete() { disposedFirst.set(dispose.get() != 0); } }); Assert.assertEquals(1, dispose.get()); Assert.assertFalse("Disposed first", disposedFirst.get()); Assert.assertNull(error.get()); } @Test(timeout = 5000) public void usingErrorEager() { final AtomicInteger dispose = new AtomicInteger(); Completable c = Completable.using(new Callable<Integer>() { @Override public Integer call() { return 1; } }, new Function<Integer, Completable>() { @Override public Completable apply(Integer v) { return error.completable; } }, new Consumer<Integer>() { @Override public void accept(Integer d) { dispose.set(d); } }); final AtomicBoolean disposedFirst = new AtomicBoolean(); final AtomicBoolean complete = new AtomicBoolean(); c.subscribe(new CompletableObserver() { @Override public void onSubscribe(Disposable d) { } @Override public void onError(Throwable e) { disposedFirst.set(dispose.get() != 0); } @Override public void onComplete() { complete.set(true); } }); Assert.assertEquals(1, dispose.get()); Assert.assertTrue("Not disposed first", disposedFirst.get()); Assert.assertFalse(complete.get()); } @Test(timeout = 5000) public void usingErrorLazy() { final AtomicInteger dispose = new AtomicInteger(); Completable c = Completable.using(new Callable<Integer>() { @Override public Integer call() { return 1; } }, new Function<Integer, Completable>() { @Override public Completable apply(Integer v) { return error.completable; } }, new Consumer<Integer>() { @Override public void accept(Integer d) { dispose.set(d); } }, false); final AtomicBoolean disposedFirst = new AtomicBoolean(); final AtomicBoolean complete = new AtomicBoolean(); c.subscribe(new CompletableObserver() { @Override public void onSubscribe(Disposable d) { } @Override public void onError(Throwable e) { disposedFirst.set(dispose.get() != 0); } @Override public void onComplete() { complete.set(true); } }); Assert.assertEquals(1, dispose.get()); Assert.assertFalse("Disposed first", disposedFirst.get()); Assert.assertFalse(complete.get()); } @Test(expected = NullPointerException.class) public void usingResourceSupplierNull() { Completable.using(null, new Function<Object, Completable>() { @Override public Completable apply(Object v) { return normal.completable; } }, new Consumer<Object>() { @Override public void accept(Object v) { } }); } @Test(expected = NullPointerException.class) public void usingMapperNull() { Completable.using(new Callable<Object>() { @Override public Object call() { return 1; } }, null, new Consumer<Object>() { @Override public void accept(Object v) { } }); } @Test(expected = NullPointerException.class) public void usingMapperReturnsNull() { Completable c = Completable.using(new Callable<Object>() { @Override public Object call() { return 1; } }, new Function<Object, Completable>() { @Override public Completable apply(Object v) { return null; } }, new Consumer<Object>() { @Override public void accept(Object v) { } }); c.blockingAwait(); } @Test(expected = NullPointerException.class) public void usingDisposeNull() { Completable.using(new Callable<Object>() { @Override public Object call() { return 1; } }, new Function<Object, Completable>() { @Override public Completable apply(Object v) { return normal.completable; } }, null); } @Test(expected = TestException.class) public void usingResourceThrows() { Completable c = Completable.using(new Callable<Object>() { @Override public Object call() { throw new TestException(); } }, new Function<Object, Completable>() { @Override public Completable apply(Object v) { return normal.completable; } }, new Consumer<Object>() { @Override public void accept(Object v) { } }); c.blockingAwait(); } @Test(expected = TestException.class) public void usingMapperThrows() { Completable c = Completable.using(new Callable<Object>() { @Override public Object call() { return 1; } }, new Function<Object, Completable>() { @Override public Completable apply(Object v) { throw new TestException(); } }, new Consumer<Object>() { @Override public void accept(Object v) { } }); c.blockingAwait(); } @Test(expected = TestException.class) public void usingDisposerThrows() { Completable c = Completable.using(new Callable<Object>() { @Override public Object call() { return 1; } }, new Function<Object, Completable>() { @Override public Completable apply(Object v) { return normal.completable; } }, new Consumer<Object>() { @Override public void accept(Object v) { throw new TestException(); } }); c.blockingAwait(); } @Test(timeout = 5000) public void composeNormal() { Completable c = error.completable.compose(new CompletableTransformer() { @Override public Completable apply(Completable n) { return n.onErrorComplete(); } }); c.blockingAwait(); } @Test(expected = NullPointerException.class) public void composeNull() { error.completable.compose(null); } @Test(timeout = 5000) public void concatWithNormal() { Completable c = normal.completable.concatWith(normal.completable); c.blockingAwait(); normal.assertSubscriptions(2); } @Test(timeout = 5000, expected = TestException.class) public void concatWithError() { Completable c = normal.completable.concatWith(error.completable); c.blockingAwait(); } @Test(expected = NullPointerException.class) public void concatWithNull() { normal.completable.concatWith(null); } @Test(expected = NullPointerException.class) public void delayUnitNull() { normal.completable.delay(1, null); } @Test(expected = NullPointerException.class) public void delaySchedulerNull() { normal.completable.delay(1, TimeUnit.SECONDS, null); } @Test(timeout = 5000) public void delayNormal() throws InterruptedException { Completable c = normal.completable.delay(250, TimeUnit.MILLISECONDS); final AtomicBoolean done = new AtomicBoolean(); final AtomicReference<Throwable> error = new AtomicReference<Throwable>(); c.subscribe(new CompletableObserver() { @Override public void onSubscribe(Disposable d) { } @Override public void onError(Throwable e) { error.set(e); } @Override public void onComplete() { done.set(true); } }); Thread.sleep(100); Assert.assertFalse("Already done", done.get()); int timeout = 10; while (timeout-- > 0 && !done.get()) { Thread.sleep(100); } Assert.assertTrue("Not done", done.get()); Assert.assertNull(error.get()); } @Test(timeout = 5000) public void delayErrorImmediately() throws InterruptedException { final TestScheduler scheduler = new TestScheduler(); final Completable c = error.completable.delay(250, TimeUnit.MILLISECONDS, scheduler); final AtomicBoolean done = new AtomicBoolean(); final AtomicReference<Throwable> error = new AtomicReference<Throwable>(); c.subscribe(new CompletableObserver() { @Override public void onSubscribe(Disposable d) { } @Override public void onError(Throwable e) { error.set(e); } @Override public void onComplete() { done.set(true); } }); scheduler.advanceTimeBy(100, TimeUnit.MILLISECONDS); Assert.assertTrue(error.get().toString(), error.get() instanceof TestException); Assert.assertFalse("Already done", done.get()); scheduler.advanceTimeBy(100, TimeUnit.MILLISECONDS); Assert.assertFalse("Already done", done.get()); } @Test(timeout = 5000) public void delayErrorToo() throws InterruptedException { Completable c = error.completable.delay(250, TimeUnit.MILLISECONDS, Schedulers.computation(), true); final AtomicBoolean done = new AtomicBoolean(); final AtomicReference<Throwable> error = new AtomicReference<Throwable>(); c.subscribe(new CompletableObserver() { @Override public void onSubscribe(Disposable d) { } @Override public void onError(Throwable e) { error.set(e); } @Override public void onComplete() { done.set(true); } }); Thread.sleep(100); Assert.assertFalse("Already done", done.get()); Assert.assertNull(error.get()); Thread.sleep(200); Assert.assertFalse("Already done", done.get()); Assert.assertTrue(error.get() instanceof TestException); } @Test(timeout = 5000) public void doOnCompleteNormal() { final AtomicInteger calls = new AtomicInteger(); Completable c = normal.completable.doOnComplete(new Action() { @Override public void run() { calls.getAndIncrement(); } }); c.blockingAwait(); Assert.assertEquals(1, calls.get()); } @Test(timeout = 5000) public void doOnCompleteError() { final AtomicInteger calls = new AtomicInteger(); Completable c = error.completable.doOnComplete(new Action() { @Override public void run() { calls.getAndIncrement(); } }); try { c.blockingAwait(); Assert.fail("Failed to throw TestException"); } catch (TestException ex) { // expected } Assert.assertEquals(0, calls.get()); } @Test(expected = NullPointerException.class) public void doOnCompleteNull() { normal.completable.doOnComplete(null); } @Test(timeout = 5000, expected = TestException.class) public void doOnCompleteThrows() { Completable c = normal.completable.doOnComplete(new Action() { @Override public void run() { throw new TestException(); } }); c.blockingAwait(); } @Test(timeout = 5000) public void doOnDisposeNormalDoesntCall() { final AtomicInteger calls = new AtomicInteger(); Completable c = normal.completable.doOnDispose(new Action() { @Override public void run() { calls.getAndIncrement(); } }); c.blockingAwait(); Assert.assertEquals(0, calls.get()); } @Test(timeout = 5000) public void doOnDisposeErrorDoesntCall() { final AtomicInteger calls = new AtomicInteger(); Completable c = error.completable.doOnDispose(new Action() { @Override public void run() { calls.getAndIncrement(); } }); try { c.blockingAwait(); Assert.fail("No exception thrown"); } catch (TestException ex) { // expected } Assert.assertEquals(0, calls.get()); } @Test(timeout = 5000) public void doOnDisposeChildCancels() { final AtomicInteger calls = new AtomicInteger(); Completable c = normal.completable.doOnDispose(new Action() { @Override public void run() { calls.getAndIncrement(); } }); c.subscribe(new CompletableObserver() { @Override public void onSubscribe(Disposable d) { d.dispose(); } @Override public void onError(Throwable e) { // ignored } @Override public void onComplete() { // ignored } }); Assert.assertEquals(1, calls.get()); } @Test(expected = NullPointerException.class) public void doOnDisposeNull() { normal.completable.doOnDispose(null); } @Test(timeout = 5000) public void doOnDisposeThrows() { Completable c = normal.completable.doOnDispose(new Action() { @Override public void run() { throw new TestException(); } }); c.subscribe(new CompletableObserver() { @Override public void onSubscribe(Disposable d) { d.dispose(); } @Override public void onError(Throwable e) { // ignored } @Override public void onComplete() { // ignored } }); } @Test(timeout = 5000) public void doOnErrorNoError() { final AtomicReference<Throwable> error = new AtomicReference<Throwable>(); Completable c = normal.completable.doOnError(new Consumer<Throwable>() { @Override public void accept(Throwable e) { error.set(e); } }); c.blockingAwait(); Assert.assertNull(error.get()); } @Test(timeout = 5000) public void doOnErrorHasError() { final AtomicReference<Throwable> err = new AtomicReference<Throwable>(); Completable c = error.completable.doOnError(new Consumer<Throwable>() { @Override public void accept(Throwable e) { err.set(e); } }); try { c.blockingAwait(); Assert.fail("Did not throw exception"); } catch (Throwable e) { // expected } Assert.assertTrue(err.get() instanceof TestException); } @Test(expected = NullPointerException.class) public void doOnErrorNull() { normal.completable.doOnError(null); } @Test(timeout = 5000) public void doOnErrorThrows() { Completable c = error.completable.doOnError(new Consumer<Throwable>() { @Override public void accept(Throwable e) { throw new IllegalStateException(); } }); try { c.blockingAwait(); } catch (CompositeException ex) { List<Throwable> a = ex.getExceptions(); Assert.assertEquals(2, a.size()); Assert.assertTrue(a.get(0) instanceof TestException); Assert.assertTrue(a.get(1) instanceof IllegalStateException); } } @Test(timeout = 5000) public void doOnSubscribeNormal() { final AtomicInteger calls = new AtomicInteger(); Completable c = normal.completable.doOnSubscribe(new Consumer<Disposable>() { @Override public void accept(Disposable s) { calls.getAndIncrement(); } }); for (int i = 0; i < 10; i++) { c.blockingAwait(); } Assert.assertEquals(10, calls.get()); } @Test(expected = NullPointerException.class) public void doOnSubscribeNull() { normal.completable.doOnSubscribe(null); } @Test(expected = TestException.class) public void doOnSubscribeThrows() { Completable c = normal.completable.doOnSubscribe(new Consumer<Disposable>() { @Override public void accept(Disposable d) { throw new TestException(); } }); c.blockingAwait(); } @Test(timeout = 5000) public void doOnTerminateNormal() { final AtomicInteger calls = new AtomicInteger(); Completable c = normal.completable.doOnTerminate(new Action() { @Override public void run() { calls.getAndIncrement(); } }); c.blockingAwait(); Assert.assertEquals(1, calls.get()); } @Test(timeout = 5000) public void doOnTerminateError() { final AtomicInteger calls = new AtomicInteger(); Completable c = error.completable.doOnTerminate(new Action() { @Override public void run() { calls.getAndIncrement(); } }); try { c.blockingAwait(); Assert.fail("Did dot throw exception"); } catch (TestException ex) { // expected } Assert.assertEquals(1, calls.get()); } @Test(timeout = 5000) public void getNormal() { Assert.assertNull(normal.completable.blockingGet()); } @Test(timeout = 5000) public void getError() { Assert.assertTrue(error.completable.blockingGet() instanceof TestException); } @Test(timeout = 5000) public void getTimeout() { try { Completable.never().blockingGet(100, TimeUnit.MILLISECONDS); } catch (RuntimeException ex) { if (!(ex.getCause() instanceof TimeoutException)) { Assert.fail("Wrong exception cause: " + ex.getCause()); } } } @Test(expected = NullPointerException.class) public void getNullUnit() { normal.completable.blockingGet(1, null); } @Test(expected = NullPointerException.class) public void liftNull() { normal.completable.lift(null); } @Test(timeout = 5000, expected = NullPointerException.class) public void liftReturnsNull() { Completable c = normal.completable.lift(new CompletableOperator() { @Override public CompletableObserver apply(CompletableObserver v) { return null; } }); c.blockingAwait(); } static final class CompletableOperatorSwap implements CompletableOperator { @Override public CompletableObserver apply(final CompletableObserver v) { return new CompletableObserver() { @Override public void onComplete() { v.onError(new TestException()); } @Override public void onError(Throwable e) { v.onComplete(); } @Override public void onSubscribe(Disposable d) { v.onSubscribe(d); } }; } } @Test(timeout = 5000, expected = TestException.class) public void liftOnCompleteError() { Completable c = normal.completable.lift(new CompletableOperatorSwap()); c.blockingAwait(); } @Test(timeout = 5000) public void liftOnErrorComplete() { Completable c = error.completable.lift(new CompletableOperatorSwap()); c.blockingAwait(); } @Test(expected = NullPointerException.class) public void mergeWithNull() { normal.completable.mergeWith(null); } @Test(timeout = 5000) public void mergeWithNormal() { Completable c = normal.completable.mergeWith(normal.completable); c.blockingAwait(); normal.assertSubscriptions(2); } @Test(expected = NullPointerException.class) public void observeOnNull() { normal.completable.observeOn(null); } @Test(timeout = 5000) public void observeOnNormal() throws InterruptedException { final AtomicReference<String> name = new AtomicReference<String>(); final AtomicReference<Throwable> err = new AtomicReference<Throwable>(); final CountDownLatch cdl = new CountDownLatch(1); Completable c = normal.completable.observeOn(Schedulers.computation()); c.subscribe(new CompletableObserver() { @Override public void onSubscribe(Disposable d) { } @Override public void onComplete() { name.set(Thread.currentThread().getName()); cdl.countDown(); } @Override public void onError(Throwable e) { err.set(e); cdl.countDown(); } }); cdl.await(); Assert.assertNull(err.get()); Assert.assertTrue(name.get().startsWith("RxComputation")); } @Test(timeout = 5000) public void observeOnError() throws InterruptedException { final AtomicReference<String> name = new AtomicReference<String>(); final AtomicReference<Throwable> err = new AtomicReference<Throwable>(); final CountDownLatch cdl = new CountDownLatch(1); Completable c = error.completable.observeOn(Schedulers.computation()); c.subscribe(new CompletableObserver() { @Override public void onSubscribe(Disposable d) { } @Override public void onComplete() { name.set(Thread.currentThread().getName()); cdl.countDown(); } @Override public void onError(Throwable e) { name.set(Thread.currentThread().getName()); err.set(e); cdl.countDown(); } }); cdl.await(); Assert.assertTrue(err.get() instanceof TestException); Assert.assertTrue(name.get().startsWith("RxComputation")); } @Test(timeout = 5000) public void onErrorComplete() { Completable c = error.completable.onErrorComplete(); c.blockingAwait(); } @Test(timeout = 5000, expected = TestException.class) public void onErrorCompleteFalse() { Completable c = error.completable.onErrorComplete(new Predicate<Throwable>() { @Override public boolean test(Throwable e) { return e instanceof IllegalStateException; } }); c.blockingAwait(); } @Test(expected = NullPointerException.class) public void onErrorCompleteNull() { error.completable.onErrorComplete(null); } @Test(expected = NullPointerException.class) public void onErrorResumeNextNull() { error.completable.onErrorResumeNext(null); } @Test(timeout = 5000) public void onErrorResumeNextFunctionReturnsNull() { Completable c = error.completable.onErrorResumeNext(new Function<Throwable, Completable>() { @Override public Completable apply(Throwable e) { return null; } }); try { c.blockingAwait(); Assert.fail("Did not throw an exception"); } catch (NullPointerException ex) { Assert.assertTrue(ex.getCause() instanceof TestException); } } @Test(timeout = 5000) public void onErrorResumeNextFunctionThrows() { Completable c = error.completable.onErrorResumeNext(new Function<Throwable, Completable>() { @Override public Completable apply(Throwable e) { throw new TestException(); } }); try { c.blockingAwait(); Assert.fail("Did not throw an exception"); } catch (CompositeException ex) { List<Throwable> a = ex.getExceptions(); Assert.assertEquals(2, a.size()); Assert.assertTrue(a.get(0) instanceof TestException); Assert.assertTrue(a.get(1) instanceof TestException); } } @Test(timeout = 5000) public void onErrorResumeNextNormal() { Completable c = error.completable.onErrorResumeNext(new Function<Throwable, Completable>() { @Override public Completable apply(Throwable v) { return normal.completable; } }); c.blockingAwait(); } @Test(timeout = 5000, expected = TestException.class) public void onErrorResumeNextError() { Completable c = error.completable.onErrorResumeNext(new Function<Throwable, Completable>() { @Override public Completable apply(Throwable v) { return error.completable; } }); c.blockingAwait(); } @Test(timeout = 2000) public void repeatNormal() { final AtomicReference<Throwable> err = new AtomicReference<Throwable>(); final AtomicInteger calls = new AtomicInteger(); Completable c = Completable.fromCallable(new Callable<Object>() { @Override public Object call() throws Exception { calls.getAndIncrement(); Thread.sleep(100); return null; } }).repeat(); c.subscribe(new CompletableObserver() { @Override public void onSubscribe(final Disposable d) { Schedulers.single().scheduleDirect(new Runnable() { @Override public void run() { d.dispose(); } }, 550, TimeUnit.MILLISECONDS); } @Override public void onError(Throwable e) { err.set(e); } @Override public void onComplete() { } }); Assert.assertEquals(6, calls.get()); Assert.assertNull(err.get()); } @Test(timeout = 5000, expected = TestException.class) public void repeatError() { Completable c = error.completable.repeat(); c.blockingAwait(); } @Test(timeout = 5000) public void repeat5Times() { final AtomicInteger calls = new AtomicInteger(); Completable c = Completable.fromCallable(new Callable<Object>() { @Override public Object call() throws Exception { calls.getAndIncrement(); return null; } }).repeat(5); c.blockingAwait(); Assert.assertEquals(5, calls.get()); } @Test(timeout = 5000) public void repeat1Time() { final AtomicInteger calls = new AtomicInteger(); Completable c = Completable.fromCallable(new Callable<Object>() { @Override public Object call() throws Exception { calls.getAndIncrement(); return null; } }).repeat(1); c.blockingAwait(); Assert.assertEquals(1, calls.get()); } @Test(timeout = 5000) public void repeat0Time() { final AtomicInteger calls = new AtomicInteger(); Completable c = Completable.fromCallable(new Callable<Object>() { @Override public Object call() throws Exception { calls.getAndIncrement(); return null; } }).repeat(0); c.blockingAwait(); Assert.assertEquals(0, calls.get()); } @Test(timeout = 5000) public void repeatUntilNormal() { final AtomicInteger calls = new AtomicInteger(); final AtomicInteger times = new AtomicInteger(5); Completable c = Completable.fromCallable(new Callable<Object>() { @Override public Object call() throws Exception { calls.getAndIncrement(); return null; } }).repeatUntil(new BooleanSupplier() { @Override public boolean getAsBoolean() { return times.decrementAndGet() == 0; } }); c.blockingAwait(); Assert.assertEquals(5, calls.get()); } @Test(expected = NullPointerException.class) public void repeatUntilNull() { normal.completable.repeatUntil(null); } @Test(expected = NullPointerException.class) public void repeatWhenNull() { normal.completable.repeatWhen(null); } @Test(timeout = 5000) public void retryNormal() { Completable c = normal.completable.retry(); c.blockingAwait(); normal.assertSubscriptions(1); } @Test(timeout = 5000) public void retry5Times() { final AtomicInteger calls = new AtomicInteger(5); Completable c = Completable.fromAction(new Action() { @Override public void run() { if (calls.decrementAndGet() != 0) { throw new TestException(); } } }).retry(); c.blockingAwait(); } @Test(timeout = 5000, expected = TestException.class) public void retryBiPredicate5Times() { Completable c = error.completable.retry(new BiPredicate<Integer, Throwable>() { @Override public boolean test(Integer n, Throwable e) { return n < 5; } }); c.blockingAwait(); } @Test(timeout = 5000, expected = TestException.class) public void retryTimes5Error() { Completable c = error.completable.retry(5); c.blockingAwait(); } @Test(timeout = 5000) public void retryTimes5Normal() { final AtomicInteger calls = new AtomicInteger(5); Completable c = Completable.fromAction(new Action() { @Override public void run() { if (calls.decrementAndGet() != 0) { throw new TestException(); } } }).retry(5); c.blockingAwait(); } @Test(expected = IllegalArgumentException.class) public void retryNegativeTimes() { normal.completable.retry(-1); } @Test(timeout = 5000, expected = TestException.class) public void retryPredicateError() { Completable c = error.completable.retry(new Predicate<Throwable>() { @Override public boolean test(Throwable e) { return false; } }); c.blockingAwait(); } @Test(expected = NullPointerException.class) public void retryPredicateNull() { error.completable.retry((Predicate<Throwable>)null); } @Test(timeout = 5000) public void retryPredicate5Times() { final AtomicInteger calls = new AtomicInteger(5); Completable c = Completable.fromAction(new Action() { @Override public void run() { if (calls.decrementAndGet() != 0) { throw new TestException(); } } }).retry(new Predicate<Throwable>() { @Override public boolean test(Throwable e) { return true; } }); c.blockingAwait(); } @Test(timeout = 5000) public void retryWhen5Times() { final AtomicInteger calls = new AtomicInteger(5); Completable c = Completable.fromAction(new Action() { @Override public void run() { if (calls.decrementAndGet() != 0) { throw new TestException(); } } }).retryWhen(new Function<Flowable<? extends Throwable>, Publisher<Object>>() { @SuppressWarnings({ "rawtypes", "unchecked" }) @Override public Publisher<Object> apply(Flowable<? extends Throwable> o) { return (Publisher)o; } }); c.blockingAwait(); } @Test(timeout = 5000) public void subscribe() throws InterruptedException { final AtomicBoolean complete = new AtomicBoolean(); Completable c = normal.completable .delay(100, TimeUnit.MILLISECONDS) .doOnComplete(new Action() { @Override public void run() { complete.set(true); } }); Disposable d = c.subscribe(); assertFalse(d.isDisposed()); Thread.sleep(150); Assert.assertTrue("Not completed", complete.get()); assertTrue(d.isDisposed()); } @Test(timeout = 5000) public void subscribeDispose() throws InterruptedException { final AtomicBoolean complete = new AtomicBoolean(); Completable c = normal.completable .delay(200, TimeUnit.MILLISECONDS) .doOnComplete(new Action() { @Override public void run() { complete.set(true); } }); Disposable d = c.subscribe(); Thread.sleep(100); d.dispose(); Thread.sleep(150); Assert.assertFalse("Completed", complete.get()); } @Test(timeout = 5000) public void subscribeTwoCallbacksNormal() { final AtomicReference<Throwable> err = new AtomicReference<Throwable>(); final AtomicBoolean complete = new AtomicBoolean(); normal.completable.subscribe(new Action() { @Override public void run() { complete.set(true); } }, new Consumer<Throwable>() { @Override public void accept(Throwable e) { err.set(e); } }); Assert.assertNull(err.get()); Assert.assertTrue("Not completed", complete.get()); } @Test(timeout = 5000) public void subscribeTwoCallbacksError() { final AtomicReference<Throwable> err = new AtomicReference<Throwable>(); final AtomicBoolean complete = new AtomicBoolean(); error.completable.subscribe(new Action() { @Override public void run() { complete.set(true); } }, new Consumer<Throwable>() { @Override public void accept(Throwable e) { err.set(e); } }); Assert.assertTrue(err.get() instanceof TestException); Assert.assertFalse("Not completed", complete.get()); } @Test(expected = NullPointerException.class) public void subscribeTwoCallbacksFirstNull() { normal.completable.subscribe(new Action() { @Override public void run() { } }, null); } @Test(expected = NullPointerException.class) public void subscribeTwoCallbacksSecondNull() { normal.completable.subscribe(new Action() { @Override public void run() { } }, null); } @Test(timeout = 5000) public void subscribeTwoCallbacksCompleteThrows() { List<Throwable> errors = TestHelper.trackPluginErrors(); try { final AtomicReference<Throwable> err = new AtomicReference<Throwable>(); normal.completable.subscribe(new Action() { @Override public void run() { throw new TestException(); } }, new Consumer<Throwable>() { @Override public void accept(Throwable e) { err.set(e); } }); Assert.assertNull(err.get()); TestHelper.assertUndeliverable(errors, 0, TestException.class); } finally { RxJavaPlugins.reset(); } } @Test(timeout = 5000) public void subscribeTwoCallbacksOnErrorThrows() { error.completable.subscribe(new Action() { @Override public void run() { } }, new Consumer<Throwable>() { @Override public void accept(Throwable e) { throw new TestException(); } }); } @Test(timeout = 5000) public void subscribeObserverNormal() { TestObserver<Object> ts = new TestObserver<Object>(); normal.completable.toObservable().subscribe(ts); ts.assertComplete(); ts.assertNoValues(); ts.assertNoErrors(); } @Test(timeout = 5000) public void subscribeObserverError() { TestObserver<Object> ts = new TestObserver<Object>(); error.completable.toObservable().subscribe(ts); ts.assertNotComplete(); ts.assertNoValues(); ts.assertError(TestException.class); } @Test(timeout = 5000) public void subscribeActionNormal() { final AtomicBoolean run = new AtomicBoolean(); normal.completable.subscribe(new Action() { @Override public void run() { run.set(true); } }); Assert.assertTrue("Not completed", run.get()); } @Test(timeout = 5000) public void subscribeActionError() { final AtomicBoolean run = new AtomicBoolean(); error.completable.subscribe(new Action() { @Override public void run() { run.set(true); } }); Assert.assertFalse("Completed", run.get()); } @Test(expected = NullPointerException.class) public void subscribeActionNull() { normal.completable.subscribe((Action)null); } @Test(expected = NullPointerException.class) public void subscribeSubscriberNull() { normal.completable.toFlowable().subscribe((Subscriber<Object>)null); } @Test(expected = NullPointerException.class) public void subscribeObserverNull() { normal.completable.toObservable().subscribe((Observer<Object>)null); } @Test(expected = NullPointerException.class) public void subscribeCompletableSubscriberNull() { normal.completable.subscribe((CompletableObserver)null); } @Test(timeout = 5000) public void subscribeSubscriberNormal() { TestSubscriber<Object> ts = new TestSubscriber<Object>(); normal.completable.toFlowable().subscribe(ts); ts.assertComplete(); ts.assertNoValues(); ts.assertNoErrors(); } @Test(timeout = 5000) public void subscribeSubscriberError() { TestSubscriber<Object> ts = new TestSubscriber<Object>(); error.completable.toFlowable().subscribe(ts); ts.assertNotComplete(); ts.assertNoValues(); ts.assertError(TestException.class); } @Test(expected = NullPointerException.class) public void subscribeOnNull() { normal.completable.subscribeOn(null); } @Test(timeout = 5000) public void subscribeOnNormal() { final AtomicReference<String> name = new AtomicReference<String>(); Completable c = Completable.unsafeCreate(new CompletableSource() { @Override public void subscribe(CompletableObserver s) { name.set(Thread.currentThread().getName()); EmptyDisposable.complete(s); } }).subscribeOn(Schedulers.computation()); c.blockingAwait(); Assert.assertTrue(name.get().startsWith("RxComputation")); } @Test(timeout = 5000) public void subscribeOnError() { final AtomicReference<String> name = new AtomicReference<String>(); Completable c = Completable.unsafeCreate(new CompletableSource() { @Override public void subscribe(CompletableObserver s) { name.set(Thread.currentThread().getName()); EmptyDisposable.error(new TestException(), s); } }).subscribeOn(Schedulers.computation()); try { c.blockingAwait(); Assert.fail("No exception thrown"); } catch (TestException ex) { // expected } Assert.assertTrue(name.get().startsWith("RxComputation")); } @Test(timeout = 5000) public void timeoutEmitError() { Throwable e = Completable.never().timeout(100, TimeUnit.MILLISECONDS).blockingGet(); Assert.assertTrue(e instanceof TimeoutException); } @Test(timeout = 5000) public void timeoutSwitchNormal() { Completable c = Completable.never().timeout(100, TimeUnit.MILLISECONDS, normal.completable); c.blockingAwait(); normal.assertSubscriptions(1); } @Test(timeout = 5000) public void timeoutTimerCancelled() throws InterruptedException { Completable c = Completable.fromCallable(new Callable<Object>() { @Override public Object call() throws Exception { Thread.sleep(50); return null; } }).timeout(100, TimeUnit.MILLISECONDS, normal.completable); c.blockingAwait(); Thread.sleep(100); normal.assertSubscriptions(0); } @Test(expected = NullPointerException.class) public void timeoutUnitNull() { normal.completable.timeout(1, null); } @Test(expected = NullPointerException.class) public void timeoutSchedulerNull() { normal.completable.timeout(1, TimeUnit.SECONDS, (Scheduler)null); } @Test(expected = NullPointerException.class) public void timeoutOtherNull() { normal.completable.timeout(1, TimeUnit.SECONDS, (Completable)null); } @Test(timeout = 5000) public void toNormal() { Flowable<Object> flow = normal.completable.to(new Function<Completable, Flowable<Object>>() { @Override public Flowable<Object> apply(Completable c) { return c.toFlowable(); } }); flow.blockingForEach(new Consumer<Object>() { @Override public void accept(Object e) { } }); } @Test(expected = NullPointerException.class) public void toNull() { normal.completable.to(null); } @Test(timeout = 5000) public void toFlowableNormal() { normal.completable.toFlowable().blockingForEach(Functions.emptyConsumer()); } @Test(timeout = 5000, expected = TestException.class) public void toFlowableError() { error.completable.toFlowable().blockingForEach(Functions.emptyConsumer()); } @Test(timeout = 5000) public void toObservableNormal() { normal.completable.toObservable().blockingForEach(Functions.emptyConsumer()); } @Test(timeout = 5000, expected = TestException.class) public void toObservableError() { error.completable.toObservable().blockingForEach(Functions.emptyConsumer()); } @Test(timeout = 5000) public void toSingleSupplierNormal() { Assert.assertEquals(1, normal.completable.toSingle(new Callable<Object>() { @Override public Object call() { return 1; } }).blockingGet()); } @Test(timeout = 5000, expected = TestException.class) public void toSingleSupplierError() { error.completable.toSingle(new Callable<Object>() { @Override public Object call() { return 1; } }).blockingGet(); } @Test(expected = NullPointerException.class) public void toSingleSupplierNull() { normal.completable.toSingle(null); } @Test(expected = NullPointerException.class) public void toSingleSupplierReturnsNull() { normal.completable.toSingle(new Callable<Object>() { @Override public Object call() { return null; } }).blockingGet(); } @Test(expected = TestException.class) public void toSingleSupplierThrows() { normal.completable.toSingle(new Callable<Object>() { @Override public Object call() { throw new TestException(); } }).blockingGet(); } @Test(timeout = 5000, expected = TestException.class) public void toSingleDefaultError() { error.completable.toSingleDefault(1).blockingGet(); } @Test(timeout = 5000) public void toSingleDefaultNormal() { Assert.assertEquals((Integer)1, normal.completable.toSingleDefault(1).blockingGet()); } @Test(expected = NullPointerException.class) public void toSingleDefaultNull() { normal.completable.toSingleDefault(null); } @Test(timeout = 5000) public void unsubscribeOnNormal() throws InterruptedException { final AtomicReference<String> name = new AtomicReference<String>(); final CountDownLatch cdl = new CountDownLatch(1); normal.completable.delay(1, TimeUnit.SECONDS) .doOnDispose(new Action() { @Override public void run() { name.set(Thread.currentThread().getName()); cdl.countDown(); } }) .unsubscribeOn(Schedulers.computation()) .subscribe(new CompletableObserver() { @Override public void onSubscribe(final Disposable d) { Schedulers.single().scheduleDirect(new Runnable() { @Override public void run() { d.dispose(); } }, 100, TimeUnit.MILLISECONDS); } @Override public void onError(Throwable e) { } @Override public void onComplete() { } }); cdl.await(); Assert.assertTrue(name.get().startsWith("RxComputation")); } @Test(expected = NullPointerException.class) public void ambArrayNull() { Completable.ambArray((Completable[])null); } @Test(timeout = 5000) public void ambArrayEmpty() { Completable c = Completable.ambArray(); c.blockingAwait(); } @Test(timeout = 5000) public void ambArraySingleNormal() { Completable c = Completable.ambArray(normal.completable); c.blockingAwait(); } @Test(timeout = 5000) public void ambArraySingleError() { Completable.ambArray(error.completable) .test() .assertError(TestException.class); } @Test(timeout = 5000) public void ambArrayOneFires() { PublishProcessor<Object> ps1 = PublishProcessor.create(); PublishProcessor<Object> ps2 = PublishProcessor.create(); Completable c1 = Completable.fromPublisher(ps1); Completable c2 = Completable.fromPublisher(ps2); Completable c = Completable.ambArray(c1, c2); final AtomicBoolean complete = new AtomicBoolean(); c.subscribe(new Action() { @Override public void run() { complete.set(true); } }); Assert.assertTrue("First subject no subscribers", ps1.hasSubscribers()); Assert.assertTrue("Second subject no subscribers", ps2.hasSubscribers()); ps1.onComplete(); Assert.assertFalse("First subject has subscribers", ps1.hasSubscribers()); Assert.assertFalse("Second subject has subscribers", ps2.hasSubscribers()); Assert.assertTrue("Not completed", complete.get()); } @Test(timeout = 5000) public void ambArrayOneFiresError() { PublishProcessor<Object> ps1 = PublishProcessor.create(); PublishProcessor<Object> ps2 = PublishProcessor.create(); Completable c1 = Completable.fromPublisher(ps1); Completable c2 = Completable.fromPublisher(ps2); Completable c = Completable.ambArray(c1, c2); final AtomicReference<Throwable> complete = new AtomicReference<Throwable>(); c.subscribe(Functions.EMPTY_ACTION, new Consumer<Throwable>() { @Override public void accept(Throwable v) { complete.set(v); } }); Assert.assertTrue("First subject no subscribers", ps1.hasSubscribers()); Assert.assertTrue("Second subject no subscribers", ps2.hasSubscribers()); ps1.onError(new TestException()); Assert.assertFalse("First subject has subscribers", ps1.hasSubscribers()); Assert.assertFalse("Second subject has subscribers", ps2.hasSubscribers()); Assert.assertTrue("Not completed", complete.get() instanceof TestException); } @Test(timeout = 5000) public void ambArraySecondFires() { PublishProcessor<Object> ps1 = PublishProcessor.create(); PublishProcessor<Object> ps2 = PublishProcessor.create(); Completable c1 = Completable.fromPublisher(ps1); Completable c2 = Completable.fromPublisher(ps2); Completable c = Completable.ambArray(c1, c2); final AtomicBoolean complete = new AtomicBoolean(); c.subscribe(new Action() { @Override public void run() { complete.set(true); } }); Assert.assertTrue("First subject no subscribers", ps1.hasSubscribers()); Assert.assertTrue("Second subject no subscribers", ps2.hasSubscribers()); ps2.onComplete(); Assert.assertFalse("First subject has subscribers", ps1.hasSubscribers()); Assert.assertFalse("Second subject has subscribers", ps2.hasSubscribers()); Assert.assertTrue("Not completed", complete.get()); } @Test(timeout = 5000) public void ambArraySecondFiresError() { PublishProcessor<Object> ps1 = PublishProcessor.create(); PublishProcessor<Object> ps2 = PublishProcessor.create(); Completable c1 = Completable.fromPublisher(ps1); Completable c2 = Completable.fromPublisher(ps2); Completable c = Completable.ambArray(c1, c2); final AtomicReference<Throwable> complete = new AtomicReference<Throwable>(); c.subscribe(Functions.EMPTY_ACTION, new Consumer<Throwable>() { @Override public void accept(Throwable v) { complete.set(v); } }); Assert.assertTrue("First subject no subscribers", ps1.hasSubscribers()); Assert.assertTrue("Second subject no subscribers", ps2.hasSubscribers()); ps2.onError(new TestException()); Assert.assertFalse("First subject has subscribers", ps1.hasSubscribers()); Assert.assertFalse("Second subject has subscribers", ps2.hasSubscribers()); Assert.assertTrue("Not completed", complete.get() instanceof TestException); } @Test(timeout = 5000) public void ambMultipleOneIsNull() { Completable.ambArray(null, normal.completable) .test() .assertError(NullPointerException.class); } @Test(timeout = 5000) public void ambIterableEmpty() { Completable c = Completable.amb(Collections.<Completable>emptyList()); c.blockingAwait(); } @Test(expected = NullPointerException.class) public void ambIterableNull() { Completable.amb((Iterable<Completable>)null); } @Test(timeout = 5000) public void ambIterableIteratorNull() { Completable.amb(new Iterable<Completable>() { @Override public Iterator<Completable> iterator() { return null; } }).test().assertError(NullPointerException.class); } @Test(timeout = 5000) public void ambIterableWithNull() { Completable.amb(Arrays.asList(null, normal.completable)) .test() .assertError(NullPointerException.class); } @Test(timeout = 5000) public void ambIterableSingle() { Completable c = Completable.amb(Collections.singleton(normal.completable)); c.blockingAwait(); normal.assertSubscriptions(1); } @Test(timeout = 5000) public void ambIterableMany() { Completable c = Completable.amb(Arrays.asList(normal.completable, normal.completable, normal.completable)); c.blockingAwait(); normal.assertSubscriptions(1); } @Test(timeout = 5000) public void ambIterableOneThrows() { Completable.amb(Collections.singleton(error.completable)) .test() .assertError(TestException.class); } @Test(timeout = 5000) public void ambIterableManyOneThrows() { Completable.amb(Arrays.asList(error.completable, normal.completable)) .test() .assertError(TestException.class); } @Test public void ambIterableIterableThrows() { Completable.amb(new Iterable<Completable>() { @Override public Iterator<Completable> iterator() { throw new TestException(); } }).test().assertError(TestException.class); } @Test public void ambIterableIteratorHasNextThrows() { Completable.amb(new IterableIteratorHasNextThrows()) .test() .assertError(TestException.class); } @Test public void ambIterableIteratorNextThrows() { Completable.amb(new IterableIteratorNextThrows()) .test() .assertError(TestException.class); } @Test(expected = NullPointerException.class) public void ambWithNull() { normal.completable.ambWith(null); } @Test(timeout = 5000) public void ambWithArrayOneFires() { PublishProcessor<Object> ps1 = PublishProcessor.create(); PublishProcessor<Object> ps2 = PublishProcessor.create(); Completable c1 = Completable.fromPublisher(ps1); Completable c2 = Completable.fromPublisher(ps2); Completable c = c1.ambWith(c2); final AtomicBoolean complete = new AtomicBoolean(); c.subscribe(new Action() { @Override public void run() { complete.set(true); } }); Assert.assertTrue("First subject no subscribers", ps1.hasSubscribers()); Assert.assertTrue("Second subject no subscribers", ps2.hasSubscribers()); ps1.onComplete(); Assert.assertFalse("First subject has subscribers", ps1.hasSubscribers()); Assert.assertFalse("Second subject has subscribers", ps2.hasSubscribers()); Assert.assertTrue("Not completed", complete.get()); } @Test(timeout = 5000) public void ambWithArrayOneFiresError() { PublishProcessor<Object> ps1 = PublishProcessor.create(); PublishProcessor<Object> ps2 = PublishProcessor.create(); Completable c1 = Completable.fromPublisher(ps1); Completable c2 = Completable.fromPublisher(ps2); Completable c = c1.ambWith(c2); final AtomicReference<Throwable> complete = new AtomicReference<Throwable>(); c.subscribe(Functions.EMPTY_ACTION, new Consumer<Throwable>() { @Override public void accept(Throwable v) { complete.set(v); } }); Assert.assertTrue("First subject no subscribers", ps1.hasSubscribers()); Assert.assertTrue("Second subject no subscribers", ps2.hasSubscribers()); ps1.onError(new TestException()); Assert.assertFalse("First subject has subscribers", ps1.hasSubscribers()); Assert.assertFalse("Second subject has subscribers", ps2.hasSubscribers()); Assert.assertTrue("Not completed", complete.get() instanceof TestException); } @Test(timeout = 5000) public void ambWithArraySecondFires() { PublishProcessor<Object> ps1 = PublishProcessor.create(); PublishProcessor<Object> ps2 = PublishProcessor.create(); Completable c1 = Completable.fromPublisher(ps1); Completable c2 = Completable.fromPublisher(ps2); Completable c = c1.ambWith(c2); final AtomicBoolean complete = new AtomicBoolean(); c.subscribe(new Action() { @Override public void run() { complete.set(true); } }); Assert.assertTrue("First subject no subscribers", ps1.hasSubscribers()); Assert.assertTrue("Second subject no subscribers", ps2.hasSubscribers()); ps2.onComplete(); Assert.assertFalse("First subject has subscribers", ps1.hasSubscribers()); Assert.assertFalse("Second subject has subscribers", ps2.hasSubscribers()); Assert.assertTrue("Not completed", complete.get()); } @Test(timeout = 5000) public void ambWithArraySecondFiresError() { PublishProcessor<Object> ps1 = PublishProcessor.create(); PublishProcessor<Object> ps2 = PublishProcessor.create(); Completable c1 = Completable.fromPublisher(ps1); Completable c2 = Completable.fromPublisher(ps2); Completable c = c1.ambWith(c2); final AtomicReference<Throwable> complete = new AtomicReference<Throwable>(); c.subscribe(Functions.EMPTY_ACTION, new Consumer<Throwable>() { @Override public void accept(Throwable v) { complete.set(v); } }); Assert.assertTrue("First subject no subscribers", ps1.hasSubscribers()); Assert.assertTrue("Second subject no subscribers", ps2.hasSubscribers()); ps2.onError(new TestException()); Assert.assertFalse("First subject has subscribers", ps1.hasSubscribers()); Assert.assertFalse("Second subject has subscribers", ps2.hasSubscribers()); Assert.assertTrue("Not completed", complete.get() instanceof TestException); } @Test(timeout = 5000) public void startWithCompletableNormal() { final AtomicBoolean run = new AtomicBoolean(); Completable c = normal.completable .startWith(Completable.fromCallable(new Callable<Object>() { @Override public Object call() throws Exception { run.set(normal.get() == 0); return null; } })); c.blockingAwait(); Assert.assertTrue("Did not start with other", run.get()); normal.assertSubscriptions(1); } @Test(timeout = 5000) public void startWithCompletableError() { Completable c = normal.completable.startWith(error.completable); try { c.blockingAwait(); Assert.fail("Did not throw TestException"); } catch (TestException ex) { normal.assertSubscriptions(0); error.assertSubscriptions(1); } } @Test(timeout = 5000) public void startWithFlowableNormal() { final AtomicBoolean run = new AtomicBoolean(); Flowable<Object> c = normal.completable .startWith(Flowable.fromCallable(new Callable<Object>() { @Override public Object call() throws Exception { run.set(normal.get() == 0); return 1; } })); TestSubscriber<Object> ts = new TestSubscriber<Object>(); c.subscribe(ts); Assert.assertTrue("Did not start with other", run.get()); normal.assertSubscriptions(1); ts.assertValue(1); ts.assertComplete(); ts.assertNoErrors(); } @Test(timeout = 5000) public void startWithFlowableError() { Flowable<Object> c = normal.completable .startWith(Flowable.error(new TestException())); TestSubscriber<Object> ts = new TestSubscriber<Object>(); c.subscribe(ts); normal.assertSubscriptions(0); ts.assertNoValues(); ts.assertError(TestException.class); ts.assertNotComplete(); } @Test(timeout = 5000) public void startWithObservableNormal() { final AtomicBoolean run = new AtomicBoolean(); Observable<Object> c = normal.completable .startWith(Observable.fromCallable(new Callable<Object>() { @Override public Object call() throws Exception { run.set(normal.get() == 0); return 1; } })); TestObserver<Object> ts = new TestObserver<Object>(); c.subscribe(ts); Assert.assertTrue("Did not start with other", run.get()); normal.assertSubscriptions(1); ts.assertValue(1); ts.assertComplete(); ts.assertNoErrors(); } @Test(timeout = 5000) public void startWithObservableError() { Observable<Object> c = normal.completable .startWith(Observable.error(new TestException())); TestObserver<Object> ts = new TestObserver<Object>(); c.subscribe(ts); normal.assertSubscriptions(0); ts.assertNoValues(); ts.assertError(TestException.class); ts.assertNotComplete(); } @Test(expected = NullPointerException.class) public void startWithCompletableNull() { normal.completable.startWith((Completable)null); } @Test(expected = NullPointerException.class) public void startWithFlowableNull() { normal.completable.startWith((Flowable<Object>)null); } @Test(expected = NullPointerException.class) public void startWithObservableNull() { normal.completable.startWith((Observable<Object>)null); } @Test public void andThen() { TestSubscriber<String> ts = new TestSubscriber<String>(0); Completable.complete().andThen(Flowable.just("foo")).subscribe(ts); ts.request(1); ts.assertValue("foo"); ts.assertComplete(); ts.assertNoErrors(); TestObserver<String> to = new TestObserver<String>(); Completable.complete().andThen(Observable.just("foo")).subscribe(to); to.assertValue("foo"); to.assertComplete(); to.assertNoErrors(); } private static void expectUncaughtTestException(Action action) { Thread.UncaughtExceptionHandler originalHandler = Thread.getDefaultUncaughtExceptionHandler(); CapturingUncaughtExceptionHandler handler = new CapturingUncaughtExceptionHandler(); Thread.setDefaultUncaughtExceptionHandler(handler); try { action.run(); assertEquals("Should have received exactly 1 exception", 1, handler.count); Throwable caught = handler.caught; while (caught != null) { if (caught instanceof TestException) { break; } if (caught == caught.getCause()) { break; } caught = caught.getCause(); } assertTrue("A TestException should have been delivered to the handler", caught instanceof TestException); } catch (Throwable ex) { throw ExceptionHelper.wrapOrThrow(ex); } finally { Thread.setDefaultUncaughtExceptionHandler(originalHandler); } } @Test public void subscribeOneActionThrowFromOnCompleted() { expectUncaughtTestException(new Action() { @Override public void run() { normal.completable.subscribe(new Action() { @Override public void run() { throw new TestException(); } }); } }); } @Test public void subscribeTwoActionsThrowFromOnError() { expectUncaughtTestException(new Action() { @Override public void run() { error.completable.subscribe( new Action() { @Override public void run() { } }, new Consumer<Throwable>() { @Override public void accept(Throwable throwable) { throw new TestException(); } }); } }); } @Test public void propagateExceptionSubscribeOneAction() { expectUncaughtTestException(new Action() { @Override public void run() { error.completable.toSingleDefault(1).subscribe(new Consumer<Integer>() { @Override public void accept(Integer integer) { } }); } }); } @Test public void usingFactoryReturnsNullAndDisposerThrows() { Consumer<Integer> onDispose = new Consumer<Integer>() { @Override public void accept(Integer t) { throw new TestException(); } }; TestSubscriber<Integer> ts = new TestSubscriber<Integer>(); Completable.using(new Callable<Integer>() { @Override public Integer call() { return 1; } }, new Function<Integer, Completable>() { @Override public Completable apply(Integer t) { return null; } }, onDispose).<Integer>toFlowable().subscribe(ts); ts.assertNoValues(); ts.assertNotComplete(); ts.assertError(CompositeException.class); CompositeException ex = (CompositeException)ts.errors().get(0); List<Throwable> listEx = ex.getExceptions(); assertEquals(2, listEx.size()); assertTrue(listEx.get(0).toString(), listEx.get(0) instanceof NullPointerException); assertTrue(listEx.get(1).toString(), listEx.get(1) instanceof TestException); } @Test public void subscribeReportsUnsubscribedOnError() { PublishSubject<String> stringSubject = PublishSubject.create(); Completable completable = stringSubject.ignoreElements(); Disposable completableSubscription = completable.subscribe(); stringSubject.onError(new TestException()); assertTrue("Not unsubscribed?", completableSubscription.isDisposed()); } @Test public void subscribeActionReportsUnsubscribed() { PublishSubject<String> stringSubject = PublishSubject.create(); Completable completable = stringSubject.ignoreElements(); Disposable completableSubscription = completable.subscribe(new Action() { @Override public void run() { } }); stringSubject.onComplete(); assertTrue("Not unsubscribed?", completableSubscription.isDisposed()); } @Test public void subscribeActionReportsUnsubscribedAfter() { PublishSubject<String> stringSubject = PublishSubject.create(); Completable completable = stringSubject.ignoreElements(); final AtomicReference<Disposable> subscriptionRef = new AtomicReference<Disposable>(); Disposable completableSubscription = completable.subscribe(new Action() { @Override public void run() { if (subscriptionRef.get().isDisposed()) { subscriptionRef.set(null); } } }); subscriptionRef.set(completableSubscription); stringSubject.onComplete(); assertTrue("Not unsubscribed?", completableSubscription.isDisposed()); assertNotNull("Unsubscribed before the call to onComplete", subscriptionRef.get()); } @Test public void subscribeActionReportsUnsubscribedOnError() { PublishSubject<String> stringSubject = PublishSubject.create(); Completable completable = stringSubject.ignoreElements(); Disposable completableSubscription = completable.subscribe(new Action() { @Override public void run() { } }); stringSubject.onError(new TestException()); assertTrue("Not unsubscribed?", completableSubscription.isDisposed()); } @Test public void subscribeAction2ReportsUnsubscribed() { PublishSubject<String> stringSubject = PublishSubject.create(); Completable completable = stringSubject.ignoreElements(); Disposable completableSubscription = completable.subscribe(new Action() { @Override public void run() { } }, new Consumer<Throwable>() { @Override public void accept(Throwable t) { } }); stringSubject.onComplete(); assertTrue("Not unsubscribed?", completableSubscription.isDisposed()); } @Test public void subscribeAction2ReportsUnsubscribedOnError() { PublishSubject<String> stringSubject = PublishSubject.create(); Completable completable = stringSubject.ignoreElements(); Disposable completableSubscription = completable.subscribe(new Action() { @Override public void run() { } }, new Consumer<Throwable>() { @Override public void accept(Throwable e) { } }); stringSubject.onError(new TestException()); assertTrue("Not unsubscribed?", completableSubscription.isDisposed()); } @Test public void andThenSubscribeOn() { TestSubscriber<String> ts = new TestSubscriber<String>(0); TestScheduler scheduler = new TestScheduler(); Completable.complete().andThen(Flowable.just("foo").delay(1, TimeUnit.SECONDS, scheduler)).subscribe(ts); ts.request(1); ts.assertNoValues(); ts.assertNotTerminated(); scheduler.advanceTimeBy(1, TimeUnit.SECONDS); ts.assertValue("foo"); ts.assertComplete(); ts.assertNoErrors(); } @Test public void andThenSingleNever() { TestSubscriber<String> ts = new TestSubscriber<String>(0); Completable.never().andThen(Single.just("foo")).toFlowable().subscribe(ts); ts.request(1); ts.assertNoValues(); ts.assertNotTerminated(); } @Test public void andThenSingleError() { TestSubscriber<String> ts = new TestSubscriber<String>(0); final AtomicBoolean hasRun = new AtomicBoolean(false); final Exception e = new Exception(); Completable.error(e) .andThen(new Single<String>() { @Override public void subscribeActual(SingleObserver<? super String> s) { hasRun.set(true); s.onSuccess("foo"); } }) .toFlowable().subscribe(ts); ts.assertNoValues(); ts.assertError(e); Assert.assertFalse("Should not have subscribed to single when completable errors", hasRun.get()); } @Test public void andThenSingleSubscribeOn() { TestSubscriber<String> ts = new TestSubscriber<String>(0); TestScheduler scheduler = new TestScheduler(); Completable.complete().andThen(Single.just("foo").delay(1, TimeUnit.SECONDS, scheduler)).toFlowable().subscribe(ts); ts.request(1); ts.assertNoValues(); ts.assertNotTerminated(); scheduler.advanceTimeBy(1, TimeUnit.SECONDS); ts.assertValue("foo"); ts.assertComplete(); ts.assertNoErrors(); } private Function<Completable, Completable> onCreate; private BiFunction<Completable, CompletableObserver, CompletableObserver> onStart; @Before public void setUp() throws Exception { onCreate = spy(new Function<Completable, Completable>() { @Override public Completable apply(Completable t) { return t; } }); RxJavaPlugins.setOnCompletableAssembly(onCreate); onStart = spy(new BiFunction<Completable, CompletableObserver, CompletableObserver>() { @Override public CompletableObserver apply(Completable t1, CompletableObserver t2) { return t2; } }); RxJavaPlugins.setOnCompletableSubscribe(onStart); } @After public void after() { RxJavaPlugins.reset(); } @Test public void testHookCreate() throws Exception { CompletableSource subscriber = mock(CompletableSource.class); Completable create = Completable.unsafeCreate(subscriber); verify(onCreate, times(1)).apply(create); } @Test(timeout = 5000) public void doOnCompletedNormal() { final AtomicInteger calls = new AtomicInteger(); Completable c = normal.completable.doOnComplete(new Action() { @Override public void run() { calls.getAndIncrement(); } }); c.blockingAwait(); Assert.assertEquals(1, calls.get()); } @Test(timeout = 5000) public void doOnCompletedError() { final AtomicInteger calls = new AtomicInteger(); Completable c = error.completable.doOnComplete(new Action() { @Override public void run() { calls.getAndIncrement(); } }); try { c.blockingAwait(); Assert.fail("Failed to throw TestException"); } catch (TestException ex) { // expected } Assert.assertEquals(0, calls.get()); } @Test(expected = NullPointerException.class) public void doOnCompletedNull() { normal.completable.doOnComplete(null); } @Test(timeout = 5000, expected = TestException.class) public void doOnCompletedThrows() { Completable c = normal.completable.doOnComplete(new Action() { @Override public void run() { throw new TestException(); } }); c.blockingAwait(); } @Test(timeout = 5000) public void doAfterTerminateNormal() { final AtomicBoolean doneAfter = new AtomicBoolean(); final AtomicBoolean complete = new AtomicBoolean(); Completable c = normal.completable.doAfterTerminate(new Action() { @Override public void run() { doneAfter.set(complete.get()); } }); c.subscribe(new CompletableObserver() { @Override public void onSubscribe(Disposable d) { } @Override public void onError(Throwable e) { } @Override public void onComplete() { complete.set(true); } }); c.blockingAwait(); Assert.assertTrue("Not completed", complete.get()); Assert.assertTrue("Closure called before onComplete", doneAfter.get()); } @Test public void doAfterTerminateWithError() { final AtomicBoolean doneAfter = new AtomicBoolean(); Completable c = error.completable.doAfterTerminate(new Action() { @Override public void run() { doneAfter.set(true); } }); try { c.blockingAwait(5, TimeUnit.SECONDS); Assert.fail("Did not throw TestException"); } catch (TestException ex) { // expected } Assert.assertTrue("Closure not called", doneAfter.get()); } @Test(expected = NullPointerException.class) public void doAfterTerminateNull() { normal.completable.doAfterTerminate(null); } @Test public void subscribeEmptyOnError() { expectUncaughtTestException(new Action() { @Override public void run() { error.completable.subscribe(); } }); } @Test public void subscribeOneActionOnError() { expectUncaughtTestException(new Action() { @Override public void run() { error.completable.subscribe(new Action() { @Override public void run() { } }); } }); } @Test public void propagateExceptionSubscribeEmpty() { expectUncaughtTestException(new Action() { @Override public void run() { error.completable.toSingleDefault(0).subscribe(); } }); } @Test(expected = NullPointerException.class) public void andThenCompletableNull() { normal.completable.andThen((Completable)null); } @Test(expected = NullPointerException.class) public void andThenFlowableNull() { normal.completable.andThen((Observable<Object>)null); } @Test(timeout = 5000) public void andThenCompletableNormal() { final AtomicBoolean run = new AtomicBoolean(); Completable c = normal.completable .andThen(Completable.fromCallable(new Callable<Object>() { @Override public Object call() throws Exception { run.set(normal.get() == 0); return null; } })); c.blockingAwait(); Assert.assertFalse("Start with other", run.get()); normal.assertSubscriptions(1); } @Test(timeout = 5000) public void andThenCompletableError() { Completable c = normal.completable.andThen(error.completable); try { c.blockingAwait(); Assert.fail("Did not throw TestException"); } catch (TestException ex) { normal.assertSubscriptions(1); error.assertSubscriptions(1); } } @Test(timeout = 5000) public void andThenFlowableNormal() { final AtomicBoolean run = new AtomicBoolean(); Flowable<Object> c = normal.completable .andThen(Flowable.fromCallable(new Callable<Object>() { @Override public Object call() throws Exception { run.set(normal.get() == 0); return 1; } })); TestSubscriber<Object> ts = new TestSubscriber<Object>(); c.subscribe(ts); Assert.assertFalse("Start with other", run.get()); normal.assertSubscriptions(1); ts.assertValue(1); ts.assertComplete(); ts.assertNoErrors(); } @Test(timeout = 5000) public void andThenFlowableError() { Flowable<Object> c = normal.completable .andThen(Flowable.error(new TestException())); TestSubscriber<Object> ts = new TestSubscriber<Object>(); c.subscribe(ts); normal.assertSubscriptions(1); ts.assertNoValues(); ts.assertError(TestException.class); ts.assertNotComplete(); } @Test public void usingFactoryThrows() throws Exception { @SuppressWarnings("unchecked") Consumer<Integer> onDispose = mock(Consumer.class); TestSubscriber<Integer> ts = new TestSubscriber<Integer>(); Completable.using(new Callable<Integer>() { @Override public Integer call() { return 1; } }, new Function<Integer, Completable>() { @Override public Completable apply(Integer t) { throw new TestException(); } }, onDispose).<Integer>toFlowable().subscribe(ts); verify(onDispose).accept(1); ts.assertNoValues(); ts.assertNotComplete(); ts.assertError(TestException.class); } @Test public void usingFactoryAndDisposerThrow() { Consumer<Integer> onDispose = new Consumer<Integer>() { @Override public void accept(Integer t) { throw new TestException(); } }; TestSubscriber<Integer> ts = new TestSubscriber<Integer>(); Completable.using(new Callable<Integer>() { @Override public Integer call() { return 1; } }, new Function<Integer, Completable>() { @Override public Completable apply(Integer t) { throw new TestException(); } }, onDispose).<Integer>toFlowable().subscribe(ts); ts.assertNoValues(); ts.assertNotComplete(); ts.assertError(CompositeException.class); CompositeException ex = (CompositeException)ts.errors().get(0); List<Throwable> listEx = ex.getExceptions(); assertEquals(2, listEx.size()); assertTrue(listEx.get(0).toString(), listEx.get(0) instanceof TestException); assertTrue(listEx.get(1).toString(), listEx.get(1) instanceof TestException); } @Test public void usingFactoryReturnsNull() throws Exception { @SuppressWarnings("unchecked") Consumer<Integer> onDispose = mock(Consumer.class); TestSubscriber<Integer> ts = TestSubscriber.create(); Completable.using(new Callable<Integer>() { @Override public Integer call() { return 1; } }, new Function<Integer, Completable>() { @Override public Completable apply(Integer t) { return null; } }, onDispose).<Integer>toFlowable().subscribe(ts); verify(onDispose).accept(1); ts.assertNoValues(); ts.assertNotComplete(); ts.assertError(NullPointerException.class); } @Test public void subscribeReportsUnsubscribed() { PublishSubject<String> stringSubject = PublishSubject.create(); Completable completable = stringSubject.ignoreElements(); Disposable completableSubscription = completable.subscribe(); stringSubject.onComplete(); assertTrue("Not unsubscribed?", completableSubscription.isDisposed()); } @Ignore("onXXX methods are not allowed to throw") @Test public void safeOnCompleteThrows() { /* try { normal.completable.subscribe(new CompletableSubscriber() { @Override public void onComplete() { throw new TestException("Forced failure"); } @Override public void onError(Throwable e) { } @Override public void onSubscribe(Subscription d) { } }); Assert.fail("Did not propagate exception!"); } catch (OnCompletedFailedException ex) { Throwable c = ex.getCause(); Assert.assertNotNull(c); Assert.assertEquals("Forced failure", c.getMessage()); } */ } @Ignore("onXXX methods are not allowed to throw") @Test public void safeOnErrorThrows() { /* try { error.completable.subscribe(new CompletableSubscriber() { @Override public void onComplete() { } @Override public void onError(Throwable e) { throw new TestException("Forced failure"); } @Override public void onSubscribe(Subscription d) { } }); Assert.fail("Did not propagate exception!"); } catch (OnErrorFailedException ex) { Throwable c = ex.getCause(); Assert.assertTrue("" + c, c instanceof CompositeException); CompositeException ce = (CompositeException)c; List<Throwable> list = ce.getExceptions(); Assert.assertEquals(2, list.size()); Assert.assertTrue("" + list.get(0), list.get(0) instanceof TestException); Assert.assertNull(list.get(0).getMessage()); Assert.assertTrue("" + list.get(1), list.get(1) instanceof TestException); Assert.assertEquals("Forced failure", list.get(1).getMessage()); } */ } @Test public void testHookSubscribeStart() throws Exception { TestSubscriber<String> ts = new TestSubscriber<String>(); Completable completable = Completable.unsafeCreate(new CompletableSource() { @Override public void subscribe(CompletableObserver s) { s.onComplete(); } }); completable.<String>toFlowable().subscribe(ts); verify(onStart, times(1)).apply(eq(completable), any(CompletableObserver.class)); } @Ignore("No unsafeSubscribe") @Test public void testHookUnsafeSubscribeStart() { /* TestSubscriber<String> ts = new TestSubscriber<String>(); Completable completable = Completable.create(new CompletableOnSubscribe() { @Override public void call(CompletableSubscriber s) { s.onComplete(); } }); completable.unsafeSubscribe(ts); verify(onStart, times(1)).call(eq(completable), any(Completable.CompletableOnSubscribe.class)); */ } @Test public void onStartCalledSafe() { TestSubscriber<Object> ts = new TestSubscriber<Object>() { @Override public void onStart() { onNext(1); } }; normal.completable.<Object>toFlowable().subscribe(ts); ts.assertValue(1); ts.assertNoErrors(); ts.assertComplete(); } @Ignore("No unsafeSubscribe") @Test public void onStartCalledUnsafeSafe() { /* TestSubscriber<Object> ts = new TestSubscriber<Object>() { @Override public void onStart() { onNext(1); } }; normal.completable.unsafeSubscribe(ts); ts.assertValue(1); ts.assertNoErrors(); ts.assertCompleted(); */ } @Test public void onErrorCompleteFunctionThrows() { TestSubscriber<String> ts = new TestSubscriber<String>(); error.completable.onErrorComplete(new Predicate<Throwable>() { @Override public boolean test(Throwable t) { throw new TestException("Forced inner failure"); } }).<String>toFlowable().subscribe(ts); ts.assertNoValues(); ts.assertNotComplete(); ts.assertError(CompositeException.class); CompositeException composite = (CompositeException)ts.errors().get(0); List<Throwable> errors = composite.getExceptions(); Assert.assertEquals(2, errors.size()); Assert.assertTrue(errors.get(0).toString(), errors.get(0) instanceof TestException); Assert.assertEquals(errors.get(0).toString(), null, errors.get(0).getMessage()); Assert.assertTrue(errors.get(1).toString(), errors.get(1) instanceof TestException); Assert.assertEquals(errors.get(1).toString(), "Forced inner failure", errors.get(1).getMessage()); } @Test public void subscribeAction2ReportsUnsubscribedAfter() { PublishSubject<String> stringSubject = PublishSubject.create(); Completable completable = stringSubject.ignoreElements(); final AtomicReference<Disposable> subscriptionRef = new AtomicReference<Disposable>(); Disposable completableSubscription = completable.subscribe(new Action() { @Override public void run() { if (subscriptionRef.get().isDisposed()) { subscriptionRef.set(null); } } }, Functions.emptyConsumer()); subscriptionRef.set(completableSubscription); stringSubject.onComplete(); assertTrue("Not unsubscribed?", completableSubscription.isDisposed()); assertNotNull("Unsubscribed before the call to onComplete", subscriptionRef.get()); } @Test public void subscribeAction2ReportsUnsubscribedOnErrorAfter() { PublishSubject<String> stringSubject = PublishSubject.create(); Completable completable = stringSubject.ignoreElements(); final AtomicReference<Disposable> subscriptionRef = new AtomicReference<Disposable>(); Disposable completableSubscription = completable.subscribe(Functions.EMPTY_ACTION, new Consumer<Throwable>() { @Override public void accept(Throwable e) { if (subscriptionRef.get().isDisposed()) { subscriptionRef.set(null); } } }); subscriptionRef.set(completableSubscription); stringSubject.onError(new TestException()); assertTrue("Not unsubscribed?", completableSubscription.isDisposed()); assertNotNull("Unsubscribed before the call to onError", subscriptionRef.get()); } @Ignore("onXXX methods are not allowed to throw") @Test public void safeOnCompleteThrowsRegularSubscriber() { /* try { normal.completable.subscribe(new FlowableSubscriber<Object>() { @Override public void onComplete() { throw new TestException("Forced failure"); } @Override public void onError(Throwable e) { } @Override public void onNext(Object t) { } }); Assert.fail("Did not propagate exception!"); } catch (OnCompletedFailedException ex) { Throwable c = ex.getCause(); Assert.assertNotNull(c); Assert.assertEquals("Forced failure", c.getMessage()); } */ } @Ignore("onXXX methods are not allowed to throw") @Test public void safeOnErrorThrowsRegularSubscriber() { /* try { error.completable.subscribe(new FlowableSubscriber<Object>() { @Override public void onComplete() { } @Override public void onError(Throwable e) { throw new TestException("Forced failure"); } @Override public void onNext(Object t) { } }); Assert.fail("Did not propagate exception!"); } catch (OnErrorFailedException ex) { Throwable c = ex.getCause(); Assert.assertTrue("" + c, c instanceof CompositeException); CompositeException ce = (CompositeException)c; List<Throwable> list = ce.getExceptions(); Assert.assertEquals(2, list.size()); Assert.assertTrue("" + list.get(0), list.get(0) instanceof TestException); Assert.assertNull(list.get(0).getMessage()); Assert.assertTrue("" + list.get(1), list.get(1) instanceof TestException); Assert.assertEquals("Forced failure", list.get(1).getMessage()); } */ } @Test public void propagateExceptionSubscribeOneActionThrowFromOnSuccess() { expectUncaughtTestException(new Action() { @Override public void run() { normal.completable.toSingleDefault(1).subscribe(new Consumer<Integer>() { @Override public void accept(Integer integer) { throw new TestException(); } }); } }); } @Test public void andThenNever() { TestSubscriber<String> ts = new TestSubscriber<String>(0); Completable.never().andThen(Flowable.just("foo")).subscribe(ts); ts.request(1); ts.assertNoValues(); ts.assertNotTerminated(); } @Test public void andThenError() { TestSubscriber<String> ts = new TestSubscriber<String>(0); final AtomicBoolean hasRun = new AtomicBoolean(false); final Exception e = new Exception(); Completable.unsafeCreate(new CompletableSource() { @Override public void subscribe(CompletableObserver cs) { cs.onError(e); } }) .andThen(Flowable.<String>unsafeCreate(new Publisher<String>() { @Override public void subscribe(Subscriber<? super String> s) { hasRun.set(true); s.onSubscribe(new BooleanSubscription()); s.onNext("foo"); s.onComplete(); } })) .subscribe(ts); ts.assertNoValues(); ts.assertError(e); Assert.assertFalse("Should not have subscribed to observable when completable errors", hasRun.get()); } @Test public void andThenSingle() { TestSubscriber<String> ts = new TestSubscriber<String>(0); Completable.complete().andThen(Single.just("foo")).toFlowable().subscribe(ts); ts.request(1); ts.assertValue("foo"); ts.assertComplete(); ts.assertNoErrors(); } @Test(expected = NullPointerException.class) public void fromFutureNull() { Completable.fromFuture(null); } @Test(timeout = 5000) public void fromFutureNormal() { ExecutorService exec = Executors.newSingleThreadExecutor(); try { Completable c = Completable.fromFuture(exec.submit(new Runnable() { @Override public void run() { // no action } })); c.blockingAwait(); } finally { exec.shutdown(); } } @Test(timeout = 5000) public void fromFutureThrows() { ExecutorService exec = Executors.newSingleThreadExecutor(); Completable c = Completable.fromFuture(exec.submit(new Runnable() { @Override public void run() { throw new TestException(); } })); try { c.blockingAwait(); Assert.fail("Failed to throw Exception"); } catch (RuntimeException ex) { if (!((ex.getCause() instanceof ExecutionException) && (ex.getCause().getCause() instanceof TestException))) { ex.printStackTrace(); Assert.fail("Wrong exception received"); } } finally { exec.shutdown(); } } @Test(expected = NullPointerException.class) public void fromRunnableNull() { Completable.fromRunnable(null); } @Test(timeout = 5000) public void fromRunnableNormal() { final AtomicInteger calls = new AtomicInteger(); Completable c = Completable.fromRunnable(new Runnable() { @Override public void run() { calls.getAndIncrement(); } }); c.blockingAwait(); Assert.assertEquals(1, calls.get()); } @Test(timeout = 5000, expected = TestException.class) public void fromRunnableThrows() { Completable c = Completable.fromRunnable(new Runnable() { @Override public void run() { throw new TestException(); } }); c.blockingAwait(); } @Test(expected = NullPointerException.class) public void doOnErrorNullValue() { Completable.complete().doOnError(null); } @Test(expected = NullPointerException.class) public void doOnSubscribeNullValue() { Completable.complete().doOnSubscribe(null); } @Test(expected = NullPointerException.class) public void doAfterTerminateNullValue() { Completable.complete().doAfterTerminate(null); } @Test(expected = NullPointerException.class) public void doOnTerminateNullValue() { Completable.complete().doOnTerminate(null); } @Test(expected = NullPointerException.class) public void doOnCompleteNullValue() { Completable.complete().doOnComplete(null); } @Test(expected = NullPointerException.class) public void doOnDisposeNullValue() { Completable.complete().doOnDispose(null); } @Test(expected = NullPointerException.class) public void doOnEventNullValue() { Completable.complete().doOnEvent(null); } @Test public void doOnEventComplete() { final AtomicInteger atomicInteger = new AtomicInteger(0); Completable.complete().doOnEvent(new Consumer<Throwable>() { @Override public void accept(final Throwable throwable) throws Exception { if (throwable == null) { atomicInteger.incrementAndGet(); } } }).subscribe(); assertEquals(1, atomicInteger.get()); } @Test public void doOnEventError() { final AtomicInteger atomicInteger = new AtomicInteger(0); Completable.error(new RuntimeException()).doOnEvent(new Consumer<Throwable>() { @Override public void accept(final Throwable throwable) throws Exception { if (throwable != null) { atomicInteger.incrementAndGet(); } } }).subscribe(); assertEquals(1, atomicInteger.get()); } @Test(timeout = 5000) public void subscribeTwoCallbacksDispose() { PublishProcessor<Integer> pp = PublishProcessor.create(); Disposable d = pp.ignoreElements().subscribe(Functions.EMPTY_ACTION, Functions.emptyConsumer()); assertFalse(d.isDisposed()); assertTrue(pp.hasSubscribers()); d.dispose(); assertTrue(d.isDisposed()); assertFalse(pp.hasSubscribers()); } }