/**
* 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.observable;
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.mockito.InOrder;
import io.reactivex.*;
import io.reactivex.Observable;
import io.reactivex.Observer;
import io.reactivex.disposables.*;
import io.reactivex.functions.*;
import io.reactivex.observables.ConnectableObservable;
import io.reactivex.observers.*;
import io.reactivex.schedulers.*;
import io.reactivex.subjects.*;
public class ObservableTest {
Observer<Number> w;
SingleObserver<Number> wo;
MaybeObserver<Number> wm;
private static final Predicate<Integer> IS_EVEN = new Predicate<Integer>() {
@Override
public boolean test(Integer v) {
return v % 2 == 0;
}
};
@Before
public void before() {
w = TestHelper.mockObserver();
wo = TestHelper.mockSingleObserver();
wm = TestHelper.mockMaybeObserver();
}
@Test
public void fromArray() {
String[] items = new String[] { "one", "two", "three" };
assertEquals((Long)3L, Observable.fromArray(items).count().blockingGet());
assertEquals("two", Observable.fromArray(items).skip(1).take(1).blockingSingle());
assertEquals("three", Observable.fromArray(items).takeLast(1).blockingSingle());
}
@Test
public void fromIterable() {
ArrayList<String> items = new ArrayList<String>();
items.add("one");
items.add("two");
items.add("three");
assertEquals((Long)3L, Observable.fromIterable(items).count().blockingGet());
assertEquals("two", Observable.fromIterable(items).skip(1).take(1).blockingSingle());
assertEquals("three", Observable.fromIterable(items).takeLast(1).blockingSingle());
}
@Test
public void fromArityArgs3() {
Observable<String> items = Observable.just("one", "two", "three");
assertEquals((Long)3L, items.count().blockingGet());
assertEquals("two", items.skip(1).take(1).blockingSingle());
assertEquals("three", items.takeLast(1).blockingSingle());
}
@Test
public void fromArityArgs1() {
Observable<String> items = Observable.just("one");
assertEquals((Long)1L, items.count().blockingGet());
assertEquals("one", items.takeLast(1).blockingSingle());
}
@Test
public void testCreate() {
Observable<String> o = Observable.just("one", "two", "three");
Observer<String> observer = TestHelper.mockObserver();
o.subscribe(observer);
verify(observer, times(1)).onNext("one");
verify(observer, times(1)).onNext("two");
verify(observer, times(1)).onNext("three");
verify(observer, never()).onError(any(Throwable.class));
verify(observer, times(1)).onComplete();
}
@Test
public void testCountAFewItemsObservable() {
Observable<String> o = Observable.just("a", "b", "c", "d");
o.count().toObservable().subscribe(w);
// we should be called only once
verify(w, times(1)).onNext(anyLong());
verify(w).onNext(4L);
verify(w, never()).onError(any(Throwable.class));
verify(w, times(1)).onComplete();
}
@Test
public void testCountZeroItemsObservable() {
Observable<String> o = Observable.empty();
o.count().toObservable().subscribe(w);
// we should be called only once
verify(w, times(1)).onNext(anyLong());
verify(w).onNext(0L);
verify(w, never()).onError(any(Throwable.class));
verify(w, times(1)).onComplete();
}
@Test
public void testCountErrorObservable() {
Observable<String> o = Observable.error(new Callable<Throwable>() {
@Override
public Throwable call() {
return new RuntimeException();
}
});
o.count().toObservable().subscribe(w);
verify(w, never()).onNext(anyInt());
verify(w, never()).onComplete();
verify(w, times(1)).onError(any(RuntimeException.class));
}
@Test
public void testCountAFewItems() {
Observable<String> o = Observable.just("a", "b", "c", "d");
o.count().subscribe(wo);
// we should be called only once
verify(wo, times(1)).onSuccess(anyLong());
verify(wo).onSuccess(4L);
verify(wo, never()).onError(any(Throwable.class));
}
@Test
public void testCountZeroItems() {
Observable<String> o = Observable.empty();
o.count().subscribe(wo);
// we should be called only once
verify(wo, times(1)).onSuccess(anyLong());
verify(wo).onSuccess(0L);
verify(wo, never()).onError(any(Throwable.class));
}
@Test
public void testCountError() {
Observable<String> o = Observable.error(new Callable<Throwable>() {
@Override
public Throwable call() {
return new RuntimeException();
}
});
o.count().subscribe(wo);
verify(wo, never()).onSuccess(anyInt());
verify(wo, times(1)).onError(any(RuntimeException.class));
}
@Test
public void testTakeFirstWithPredicateOfSome() {
Observable<Integer> o = Observable.just(1, 3, 5, 4, 6, 3);
o.filter(IS_EVEN).take(1).subscribe(w);
verify(w, times(1)).onNext(anyInt());
verify(w).onNext(4);
verify(w, times(1)).onComplete();
verify(w, never()).onError(any(Throwable.class));
}
@Test
public void testTakeFirstWithPredicateOfNoneMatchingThePredicate() {
Observable<Integer> o = Observable.just(1, 3, 5, 7, 9, 7, 5, 3, 1);
o.filter(IS_EVEN).take(1).subscribe(w);
verify(w, never()).onNext(anyInt());
verify(w, times(1)).onComplete();
verify(w, never()).onError(any(Throwable.class));
}
@Test
public void testTakeFirstOfSome() {
Observable<Integer> o = Observable.just(1, 2, 3);
o.take(1).subscribe(w);
verify(w, times(1)).onNext(anyInt());
verify(w).onNext(1);
verify(w, times(1)).onComplete();
verify(w, never()).onError(any(Throwable.class));
}
@Test
public void testTakeFirstOfNone() {
Observable<Integer> o = Observable.empty();
o.take(1).subscribe(w);
verify(w, never()).onNext(anyInt());
verify(w, times(1)).onComplete();
verify(w, never()).onError(any(Throwable.class));
}
@Test
public void testFirstOfNone() {
Observable<Integer> o = Observable.empty();
o.firstElement().subscribe(wm);
verify(wm, never()).onSuccess(anyInt());
verify(wm).onComplete();
verify(wm, never()).onError(any(Throwable.class));
}
@Test
public void testFirstWithPredicateOfNoneMatchingThePredicate() {
Observable<Integer> o = Observable.just(1, 3, 5, 7, 9, 7, 5, 3, 1);
o.filter(IS_EVEN).firstElement().subscribe(wm);
verify(wm, never()).onSuccess(anyInt());
verify(wm).onComplete();
verify(wm, never()).onError(any(Throwable.class));
}
@Test
public void testReduce() {
Observable<Integer> o = Observable.just(1, 2, 3, 4);
o.reduce(new BiFunction<Integer, Integer, Integer>() {
@Override
public Integer apply(Integer t1, Integer t2) {
return t1 + t2;
}
})
.subscribe(wm);
// we should be called only once
verify(wm, times(1)).onSuccess(anyInt());
verify(wm).onSuccess(10);
verify(wm, never()).onError(any(Throwable.class));
verify(wm, never()).onComplete();
}
@Test
public void testReduceObservable() {
Observable<Integer> o = Observable.just(1, 2, 3, 4);
o.reduce(new BiFunction<Integer, Integer, Integer>() {
@Override
public Integer apply(Integer t1, Integer t2) {
return t1 + t2;
}
})
.toObservable()
.subscribe(w);
// we should be called only once
verify(w, times(1)).onNext(anyInt());
verify(w).onNext(10);
verify(w, never()).onError(any(Throwable.class));
verify(w).onComplete();
}
@Test
public void testReduceWithEmptyObservable() {
Observable<Integer> o = Observable.range(1, 0);
o.reduce(new BiFunction<Integer, Integer, Integer>() {
@Override
public Integer apply(Integer t1, Integer t2) {
return t1 + t2;
}
})
.toObservable()
.test()
.assertResult();
}
/**
* A reduce on an empty Observable and a seed should just pass the seed through.
*
* This is confirmed at https://github.com/ReactiveX/RxJava/issues/423#issuecomment-27642456
*/
@Test
public void testReduceWithEmptyObservableAndSeed() {
Observable<Integer> o = Observable.range(1, 0);
int value = o.reduce(1, new BiFunction<Integer, Integer, Integer>() {
@Override
public Integer apply(Integer t1, Integer t2) {
return t1 + t2;
}
})
.blockingGet();
assertEquals(1, value);
}
@Test
public void testReduceWithInitialValue() {
Observable<Integer> o = Observable.just(1, 2, 3, 4);
o.reduce(50, new BiFunction<Integer, Integer, Integer>() {
@Override
public Integer apply(Integer t1, Integer t2) {
return t1 + t2;
}
})
.subscribe(wo);
// we should be called only once
verify(wo, times(1)).onSuccess(anyInt());
verify(wo).onSuccess(60);
verify(wo, never()).onError(any(Throwable.class));
}
@Test
public void testReduceWithInitialValueObservable() {
Observable<Integer> o = Observable.just(1, 2, 3, 4);
o.reduce(50, new BiFunction<Integer, Integer, Integer>() {
@Override
public Integer apply(Integer t1, Integer t2) {
return t1 + t2;
}
})
.toObservable()
.subscribe(w);
// we should be called only once
verify(w, times(1)).onNext(anyInt());
verify(w).onNext(60);
}
@Ignore("Throwing is not allowed from the unsafeCreate?!")
@Test // FIXME throwing is not allowed from the create?!
public void testOnSubscribeFails() {
Observer<String> observer = TestHelper.mockObserver();
final RuntimeException re = new RuntimeException("bad impl");
Observable<String> o = Observable.unsafeCreate(new ObservableSource<String>() {
@Override
public void subscribe(Observer<? super String> s) { throw re; }
});
o.subscribe(observer);
verify(observer, times(0)).onNext(anyString());
verify(observer, times(0)).onComplete();
verify(observer, times(1)).onError(re);
}
@Test
public void testMaterializeDematerializeChaining() {
Observable<Integer> obs = Observable.just(1);
Observable<Integer> chained = obs.materialize().dematerialize();
Observer<Integer> observer = TestHelper.mockObserver();
chained.subscribe(observer);
verify(observer, times(1)).onNext(1);
verify(observer, times(1)).onComplete();
verify(observer, times(0)).onError(any(Throwable.class));
}
/**
* The error from the user provided Observer is not handled by the subscribe method try/catch.
*
* It is handled by the AtomicObserver that wraps the provided Observer.
*
* Result: Passes (if AtomicObserver functionality exists)
* @throws InterruptedException if the test is interrupted
*/
@Test
public void testCustomObservableWithErrorInObserverAsynchronous() throws InterruptedException {
final CountDownLatch latch = new CountDownLatch(1);
final AtomicInteger count = new AtomicInteger();
final AtomicReference<Throwable> error = new AtomicReference<Throwable>();
// FIXME custom built???
Observable.just("1", "2", "three", "4")
.subscribeOn(Schedulers.newThread())
.safeSubscribe(new DefaultObserver<String>() {
@Override
public void onComplete() {
System.out.println("completed");
latch.countDown();
}
@Override
public void onError(Throwable e) {
error.set(e);
System.out.println("error");
e.printStackTrace();
latch.countDown();
}
@Override
public void onNext(String v) {
int num = Integer.parseInt(v);
System.out.println(num);
// doSomething(num);
count.incrementAndGet();
}
});
// wait for async sequence to complete
latch.await();
assertEquals(2, count.get());
assertNotNull(error.get());
if (!(error.get() instanceof NumberFormatException)) {
fail("It should be a NumberFormatException");
}
}
/**
* The error from the user provided Observer is handled by the subscribe try/catch because this is synchronous.
*
* Result: Passes
*/
@Test
public void testCustomObservableWithErrorInObserverSynchronous() {
final AtomicInteger count = new AtomicInteger();
final AtomicReference<Throwable> error = new AtomicReference<Throwable>();
// FIXME custom built???
Observable.just("1", "2", "three", "4")
.safeSubscribe(new DefaultObserver<String>() {
@Override
public void onComplete() {
System.out.println("completed");
}
@Override
public void onError(Throwable e) {
error.set(e);
System.out.println("error");
e.printStackTrace();
}
@Override
public void onNext(String v) {
int num = Integer.parseInt(v);
System.out.println(num);
// doSomething(num);
count.incrementAndGet();
}
});
assertEquals(2, count.get());
assertNotNull(error.get());
if (!(error.get() instanceof NumberFormatException)) {
fail("It should be a NumberFormatException");
}
}
/**
* The error from the user provided Observable is handled by the subscribe try/catch because this is synchronous.
*
*
* Result: Passes
*/
@Test
public void testCustomObservableWithErrorInObservableSynchronous() {
final AtomicInteger count = new AtomicInteger();
final AtomicReference<Throwable> error = new AtomicReference<Throwable>();
// FIXME custom built???
Observable.just("1", "2").concatWith(Observable.<String>error(new Callable<Throwable>() {
@Override
public Throwable call() {
return new NumberFormatException();
}
}))
.subscribe(new DefaultObserver<String>() {
@Override
public void onComplete() {
System.out.println("completed");
}
@Override
public void onError(Throwable e) {
error.set(e);
System.out.println("error");
e.printStackTrace();
}
@Override
public void onNext(String v) {
System.out.println(v);
count.incrementAndGet();
}
});
assertEquals(2, count.get());
assertNotNull(error.get());
if (!(error.get() instanceof NumberFormatException)) {
fail("It should be a NumberFormatException");
}
}
@Test
public void testPublishLast() throws InterruptedException {
final AtomicInteger count = new AtomicInteger();
ConnectableObservable<String> connectable = Observable.<String>unsafeCreate(new ObservableSource<String>() {
@Override
public void subscribe(final Observer<? super String> observer) {
observer.onSubscribe(Disposables.empty());
count.incrementAndGet();
new Thread(new Runnable() {
@Override
public void run() {
observer.onNext("first");
observer.onNext("last");
observer.onComplete();
}
}).start();
}
}).takeLast(1).publish();
// subscribe once
final CountDownLatch latch = new CountDownLatch(1);
connectable.subscribe(new Consumer<String>() {
@Override
public void accept(String value) {
assertEquals("last", value);
latch.countDown();
}
});
// subscribe twice
connectable.subscribe();
Disposable subscription = connectable.connect();
assertTrue(latch.await(1000, TimeUnit.MILLISECONDS));
assertEquals(1, count.get());
subscription.dispose();
}
@Test
public void testReplay() throws InterruptedException {
final AtomicInteger counter = new AtomicInteger();
ConnectableObservable<String> o = Observable.<String>unsafeCreate(new ObservableSource<String>() {
@Override
public void subscribe(final Observer<? super String> observer) {
observer.onSubscribe(Disposables.empty());
new Thread(new Runnable() {
@Override
public void run() {
counter.incrementAndGet();
observer.onNext("one");
observer.onComplete();
}
}).start();
}
}).replay();
// we connect immediately and it will emit the value
Disposable s = o.connect();
try {
// we then expect the following 2 subscriptions to get that same value
final CountDownLatch latch = new CountDownLatch(2);
// subscribe once
o.subscribe(new Consumer<String>() {
@Override
public void accept(String v) {
assertEquals("one", v);
latch.countDown();
}
});
// subscribe again
o.subscribe(new Consumer<String>() {
@Override
public void accept(String v) {
assertEquals("one", v);
latch.countDown();
}
});
if (!latch.await(1000, TimeUnit.MILLISECONDS)) {
fail("subscriptions did not receive values");
}
assertEquals(1, counter.get());
} finally {
s.dispose();
}
}
@Test
public void testCache() throws InterruptedException {
final AtomicInteger counter = new AtomicInteger();
Observable<String> o = Observable.<String>unsafeCreate(new ObservableSource<String>() {
@Override
public void subscribe(final Observer<? super String> observer) {
observer.onSubscribe(Disposables.empty());
new Thread(new Runnable() {
@Override
public void run() {
counter.incrementAndGet();
observer.onNext("one");
observer.onComplete();
}
}).start();
}
}).cache();
// we then expect the following 2 subscriptions to get that same value
final CountDownLatch latch = new CountDownLatch(2);
// subscribe once
o.subscribe(new Consumer<String>() {
@Override
public void accept(String v) {
assertEquals("one", v);
latch.countDown();
}
});
// subscribe again
o.subscribe(new Consumer<String>() {
@Override
public void accept(String v) {
assertEquals("one", v);
latch.countDown();
}
});
if (!latch.await(1000, TimeUnit.MILLISECONDS)) {
fail("subscriptions did not receive values");
}
assertEquals(1, counter.get());
}
@Test
public void testCacheWithCapacity() throws InterruptedException {
final AtomicInteger counter = new AtomicInteger();
Observable<String> o = Observable.<String>unsafeCreate(new ObservableSource<String>() {
@Override
public void subscribe(final Observer<? super String> observer) {
observer.onSubscribe(Disposables.empty());
new Thread(new Runnable() {
@Override
public void run() {
counter.incrementAndGet();
observer.onNext("one");
observer.onComplete();
}
}).start();
}
}).cacheWithInitialCapacity(1);
// we then expect the following 2 subscriptions to get that same value
final CountDownLatch latch = new CountDownLatch(2);
// subscribe once
o.subscribe(new Consumer<String>() {
@Override
public void accept(String v) {
assertEquals("one", v);
latch.countDown();
}
});
// subscribe again
o.subscribe(new Consumer<String>() {
@Override
public void accept(String v) {
assertEquals("one", v);
latch.countDown();
}
});
if (!latch.await(1000, TimeUnit.MILLISECONDS)) {
fail("subscriptions did not receive values");
}
assertEquals(1, counter.get());
}
/**
* https://github.com/ReactiveX/RxJava/issues/198
*
* Rx Design Guidelines 5.2
*
* "when calling the Subscribe method that only has an onNext argument, the OnError behavior will be
* to rethrow the exception on the thread that the message comes out from the Observable.
* The OnCompleted behavior in this case is to do nothing."
*/
@Test
@Ignore("Subscribers can't throw")
public void testErrorThrownWithoutErrorHandlerSynchronous() {
try {
Observable.error(new RuntimeException("failure"))
.subscribe();
fail("expected exception");
} catch (Throwable e) {
assertEquals("failure", e.getMessage());
}
}
/**
* https://github.com/ReactiveX/RxJava/issues/198
*
* Rx Design Guidelines 5.2
*
* "when calling the Subscribe method that only has an onNext argument, the OnError behavior will be
* to rethrow the exception on the thread that the message comes out from the Observable.
* The OnCompleted behavior in this case is to do nothing."
*
* @throws InterruptedException if the await is interrupted
*/
@Test
@Ignore("Subscribers can't throw")
public void testErrorThrownWithoutErrorHandlerAsynchronous() throws InterruptedException {
final CountDownLatch latch = new CountDownLatch(1);
final AtomicReference<Throwable> exception = new AtomicReference<Throwable>();
Observable.unsafeCreate(new ObservableSource<Object>() {
@Override
public void subscribe(final Observer<? super Object> observer) {
new Thread(new Runnable() {
@Override
public void run() {
try {
observer.onError(new Error("failure"));
} catch (Throwable e) {
// without an onError handler it has to just throw on whatever thread invokes it
exception.set(e);
}
latch.countDown();
}
}).start();
}
}).subscribe();
// wait for exception
latch.await(3000, TimeUnit.MILLISECONDS);
assertNotNull(exception.get());
assertEquals("failure", exception.get().getMessage());
}
@Test
public void testTakeWithErrorInObserver() {
final AtomicInteger count = new AtomicInteger();
final AtomicReference<Throwable> error = new AtomicReference<Throwable>();
Observable.just("1", "2", "three", "4").take(3)
.safeSubscribe(new DefaultObserver<String>() {
@Override
public void onComplete() {
System.out.println("completed");
}
@Override
public void onError(Throwable e) {
error.set(e);
System.out.println("error");
e.printStackTrace();
}
@Override
public void onNext(String v) {
int num = Integer.parseInt(v);
System.out.println(num);
// doSomething(num);
count.incrementAndGet();
}
});
assertEquals(2, count.get());
assertNotNull(error.get());
if (!(error.get() instanceof NumberFormatException)) {
fail("It should be a NumberFormatException");
}
}
@Test
public void testOfType() {
Observable<String> o = Observable.just(1, "abc", false, 2L).ofType(String.class);
Observer<Object> observer = TestHelper.mockObserver();
o.subscribe(observer);
verify(observer, never()).onNext(1);
verify(observer, times(1)).onNext("abc");
verify(observer, never()).onNext(false);
verify(observer, never()).onNext(2L);
verify(observer, never()).onError(
any(Throwable.class));
verify(observer, times(1)).onComplete();
}
@Test
public void testOfTypeWithPolymorphism() {
ArrayList<Integer> l1 = new ArrayList<Integer>();
l1.add(1);
LinkedList<Integer> l2 = new LinkedList<Integer>();
l2.add(2);
@SuppressWarnings("rawtypes")
Observable<List> o = Observable.<Object> just(l1, l2, "123").ofType(List.class);
Observer<Object> observer = TestHelper.mockObserver();
o.subscribe(observer);
verify(observer, times(1)).onNext(l1);
verify(observer, times(1)).onNext(l2);
verify(observer, never()).onNext("123");
verify(observer, never()).onError(
any(Throwable.class));
verify(observer, times(1)).onComplete();
}
@Test
public void testContainsObservable() {
Observable<Boolean> o = Observable.just("a", "b", "c").contains("b").toObservable();
Observer<Boolean> observer = TestHelper.mockObserver();
o.subscribe(observer);
verify(observer, times(1)).onNext(true);
verify(observer, never()).onNext(false);
verify(observer, never()).onError(
any(Throwable.class));
verify(observer, times(1)).onComplete();
}
@Test
public void testContainsWithInexistenceObservable() {
Observable<Boolean> o = Observable.just("a", "b").contains("c").toObservable();
Observer<Object> observer = TestHelper.mockObserver();
o.subscribe(observer);
verify(observer, times(1)).onNext(false);
verify(observer, never()).onNext(true);
verify(observer, never()).onError(
any(Throwable.class));
verify(observer, times(1)).onComplete();
}
@Test
@Ignore("null values are not allowed")
public void testContainsWithNullObservable() {
Observable<Boolean> o = Observable.just("a", "b", null).contains(null).toObservable();
Observer<Object> observer = TestHelper.mockObserver();
o.subscribe(observer);
verify(observer, times(1)).onNext(true);
verify(observer, never()).onNext(false);
verify(observer, never()).onError(
any(Throwable.class));
verify(observer, times(1)).onComplete();
}
@Test
public void testContainsWithEmptyObservableObservable() {
Observable<Boolean> o = Observable.<String> empty().contains("a").toObservable();
Observer<Object> observer = TestHelper.mockObserver();
o.subscribe(observer);
verify(observer, times(1)).onNext(false);
verify(observer, never()).onNext(true);
verify(observer, never()).onError(
any(Throwable.class));
verify(observer, times(1)).onComplete();
}
@Test
public void testContains() {
Single<Boolean> o = Observable.just("a", "b", "c").contains("b"); // FIXME nulls not allowed, changed to "c"
SingleObserver<Boolean> observer = TestHelper.mockSingleObserver();
o.subscribe(observer);
verify(observer, times(1)).onSuccess(true);
verify(observer, never()).onSuccess(false);
verify(observer, never()).onError(
any(Throwable.class));
}
@Test
public void testContainsWithInexistence() {
Single<Boolean> o = Observable.just("a", "b").contains("c"); // FIXME null values are not allowed, removed
SingleObserver<Object> observer = TestHelper.mockSingleObserver();
o.subscribe(observer);
verify(observer, times(1)).onSuccess(false);
verify(observer, never()).onSuccess(true);
verify(observer, never()).onError(
any(Throwable.class));
}
@Test
@Ignore("null values are not allowed")
public void testContainsWithNull() {
Single<Boolean> o = Observable.just("a", "b", null).contains(null);
SingleObserver<Object> observer = TestHelper.mockSingleObserver();
o.subscribe(observer);
verify(observer, times(1)).onSuccess(true);
verify(observer, never()).onSuccess(false);
verify(observer, never()).onError(
any(Throwable.class));
}
@Test
public void testContainsWithEmptyObservable() {
Single<Boolean> o = Observable.<String> empty().contains("a");
SingleObserver<Object> observer = TestHelper.mockSingleObserver();
o.subscribe(observer);
verify(observer, times(1)).onSuccess(false);
verify(observer, never()).onSuccess(true);
verify(observer, never()).onError(
any(Throwable.class));
}
@Test
public void testIgnoreElements() {
Completable o = Observable.just(1, 2, 3).ignoreElements();
CompletableObserver observer = TestHelper.mockCompletableObserver();
o.subscribe(observer);
verify(observer, never()).onError(any(Throwable.class));
verify(observer, times(1)).onComplete();
}
@Test
public void testIgnoreElementsObservable() {
Observable<Integer> o = Observable.just(1, 2, 3).ignoreElements().toObservable();
Observer<Object> observer = TestHelper.mockObserver();
o.subscribe(observer);
verify(observer, never()).onNext(any(Integer.class));
verify(observer, never()).onError(any(Throwable.class));
verify(observer, times(1)).onComplete();
}
@Test
public void testJustWithScheduler() {
TestScheduler scheduler = new TestScheduler();
Observable<Integer> o = Observable.fromArray(1, 2).subscribeOn(scheduler);
Observer<Integer> observer = TestHelper.mockObserver();
o.subscribe(observer);
scheduler.advanceTimeBy(1, TimeUnit.MILLISECONDS);
InOrder inOrder = inOrder(observer);
inOrder.verify(observer, times(1)).onNext(1);
inOrder.verify(observer, times(1)).onNext(2);
inOrder.verify(observer, times(1)).onComplete();
inOrder.verifyNoMoreInteractions();
}
@Test
public void testStartWithWithScheduler() {
TestScheduler scheduler = new TestScheduler();
Observable<Integer> o = Observable.just(3, 4).startWith(Arrays.asList(1, 2)).subscribeOn(scheduler);
Observer<Integer> observer = TestHelper.mockObserver();
o.subscribe(observer);
scheduler.advanceTimeBy(1, TimeUnit.MILLISECONDS);
InOrder inOrder = inOrder(observer);
inOrder.verify(observer, times(1)).onNext(1);
inOrder.verify(observer, times(1)).onNext(2);
inOrder.verify(observer, times(1)).onNext(3);
inOrder.verify(observer, times(1)).onNext(4);
inOrder.verify(observer, times(1)).onComplete();
inOrder.verifyNoMoreInteractions();
}
@Test
public void testRangeWithScheduler() {
TestScheduler scheduler = new TestScheduler();
Observable<Integer> o = Observable.range(3, 4).subscribeOn(scheduler);
Observer<Integer> observer = TestHelper.mockObserver();
o.subscribe(observer);
scheduler.advanceTimeBy(1, TimeUnit.MILLISECONDS);
InOrder inOrder = inOrder(observer);
inOrder.verify(observer, times(1)).onNext(3);
inOrder.verify(observer, times(1)).onNext(4);
inOrder.verify(observer, times(1)).onNext(5);
inOrder.verify(observer, times(1)).onNext(6);
inOrder.verify(observer, times(1)).onComplete();
inOrder.verifyNoMoreInteractions();
}
@Test
public void testMergeWith() {
TestObserver<Integer> ts = new TestObserver<Integer>();
Observable.just(1).mergeWith(Observable.just(2)).subscribe(ts);
ts.assertValues(1, 2);
}
@Test
public void testConcatWith() {
TestObserver<Integer> ts = new TestObserver<Integer>();
Observable.just(1).concatWith(Observable.just(2)).subscribe(ts);
ts.assertValues(1, 2);
}
@Test
public void testAmbWith() {
TestObserver<Integer> ts = new TestObserver<Integer>();
Observable.just(1).ambWith(Observable.just(2)).subscribe(ts);
ts.assertValue(1);
}
// FIXME Subscribers can't throw
// @Test(expected = OnErrorNotImplementedException.class)
// public void testSubscribeWithoutOnError() {
// Observable<String> o = Observable.just("a", "b").flatMap(new Func1<String, Observable<String>>() {
// @Override
// public Observable<String> call(String s) {
// return Observable.error(new Exception("test"));
// }
// });
// o.subscribe();
// }
@Test
public void testTakeWhileToList() {
final int expectedCount = 3;
final AtomicInteger count = new AtomicInteger();
for (int i = 0;i < expectedCount; i++) {
Observable
.just(Boolean.TRUE, Boolean.FALSE)
.takeWhile(new Predicate<Boolean>() {
@Override
public boolean test(Boolean v) {
return v;
}
})
.toList()
.doOnSuccess(new Consumer<List<Boolean>>() {
@Override
public void accept(List<Boolean> booleans) {
count.incrementAndGet();
}
})
.subscribe();
}
assertEquals(expectedCount, count.get());
}
@Test
public void testCompose() {
TestObserver<String> ts = new TestObserver<String>();
Observable.just(1, 2, 3).compose(new ObservableTransformer<Integer, String>() {
@Override
public Observable<String> apply(Observable<Integer> t1) {
return t1.map(new Function<Integer, String>() {
@Override
public String apply(Integer v) {
return String.valueOf(v);
}
});
}
})
.subscribe(ts);
ts.assertTerminated();
ts.assertNoErrors();
ts.assertValues("1", "2", "3");
}
@Test
public void testErrorThrownIssue1685() {
Subject<Object> subject = ReplaySubject.create();
Observable.error(new RuntimeException("oops"))
.materialize()
.delay(1, TimeUnit.SECONDS)
.dematerialize()
.subscribe(subject);
subject.subscribe();
subject.materialize().blockingFirst();
System.out.println("Done");
}
@Test
public void testEmptyIdentity() {
assertEquals(Observable.empty(), Observable.empty());
}
@Test
public void testEmptyIsEmpty() {
Observable.<Integer>empty().subscribe(w);
verify(w).onComplete();
verify(w, never()).onNext(any(Integer.class));
verify(w, never()).onError(any(Throwable.class));
}
// FIXME this test doesn't make sense
// @Test // cf. https://github.com/ReactiveX/RxJava/issues/2599
// public void testSubscribingSubscriberAsObserverMaintainsSubscriptionChain() {
// TestObserver<Object> observer = new TestObserver<T>();
// Subscription subscription = Observable.just("event").subscribe((Observer<Object>) observer);
// subscription.unsubscribe();
//
// subscriber.assertUnsubscribed();
// }
// FIXME subscribers can't throw
// @Test(expected=OnErrorNotImplementedException.class)
// public void testForEachWithError() {
// Observable.error(new Exception("boo"))
// //
// .forEach(new Action1<Object>() {
// @Override
// public void call(Object t) {
// //do nothing
// }});
// }
@Test(expected = NullPointerException.class)
public void testForEachWithNull() {
Observable.error(new Exception("boo"))
//
.forEach(null);
}
@Test
public void testExtend() {
final TestObserver<Object> subscriber = new TestObserver<Object>();
final Object value = new Object();
Observable.just(value).to(new Function<Observable<Object>, Object>() {
@Override
public Object apply(Observable<Object> onSubscribe) {
onSubscribe.subscribe(subscriber);
subscriber.assertNoErrors();
subscriber.assertComplete();
subscriber.assertValue(value);
return subscriber.values().get(0);
}
});
}
@Test
public void testFlatMap() {
List<Integer> list = Observable.range(1, 5).flatMap(new Function<Integer, Observable<Integer>>() {
@Override
public Observable<Integer> apply(Integer v) {
return Observable.range(v, 2);
}
}).toList().blockingGet();
Assert.assertEquals(Arrays.asList(1, 2, 2, 3, 3, 4, 4, 5, 5, 6), list);
}
@Test
public void singleDefault() {
Observable.just(1).single(100).test().assertResult(1);
Observable.empty().single(100).test().assertResult(100);
}
@Test
public void singleDefaultObservable() {
Observable.just(1).single(100).toObservable().test().assertResult(1);
Observable.empty().single(100).toObservable().test().assertResult(100);
}
@Test
public void zipIterableObject() {
@SuppressWarnings("unchecked")
final List<Observable<Integer>> observables = Arrays.asList(Observable.just(1, 2, 3), Observable.just(1, 2, 3));
Observable.zip(observables, new Function<Object[], Object>() {
@Override
public Object apply(Object[] o) throws Exception {
int sum = 0;
for (Object i : o) {
sum += (Integer) i;
}
return sum;
}
}).test().assertResult(2, 4, 6);
}
@Test
public void combineLatestObject() {
@SuppressWarnings("unchecked")
final List<Observable<Integer>> observables = Arrays.asList(Observable.just(1, 2, 3), Observable.just(1, 2, 3));
Observable.combineLatest(observables, new Function<Object[], Object>() {
@Override
public Object apply(final Object[] o) throws Exception {
int sum = 1;
for (Object i : o) {
sum *= (Integer) i;
}
return sum;
}
}).test().assertResult(3, 6, 9);
}
}