/** * Copyright 2014 Netflix, Inc. * * 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 rx; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.isA; import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import java.util.ArrayList; import java.util.Arrays; import java.util.LinkedList; import java.util.List; import java.util.NoSuchElementException; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; import org.junit.Before; import org.junit.Test; import org.mockito.InOrder; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import rx.Observable.OnSubscribe; import rx.Observable.Transformer; import rx.exceptions.OnErrorNotImplementedException; import rx.functions.Action1; import rx.functions.Action2; import rx.functions.Func0; import rx.functions.Func1; import rx.functions.Func2; import rx.observables.ConnectableObservable; import rx.observers.TestSubscriber; import rx.schedulers.TestScheduler; import rx.subjects.ReplaySubject; import rx.subjects.Subject; import rx.subscriptions.BooleanSubscription; public class ObservableTests { @Mock Observer<Integer> w; private static final Func1<Integer, Boolean> IS_EVEN = new Func1<Integer, Boolean>() { @Override public Boolean call(Integer value) { return value % 2 == 0; } }; @Before public void before() { MockitoAnnotations.initMocks(this); } @Test public void fromArray() { String[] items = new String[] { "one", "two", "three" }; assertEquals(new Integer(3), Observable.from(items).count().toBlocking().single()); assertEquals("two", Observable.from(items).skip(1).take(1).toBlocking().single()); assertEquals("three", Observable.from(items).takeLast(1).toBlocking().single()); } @Test public void fromIterable() { ArrayList<String> items = new ArrayList<String>(); items.add("one"); items.add("two"); items.add("three"); assertEquals(new Integer(3), Observable.from(items).count().toBlocking().single()); assertEquals("two", Observable.from(items).skip(1).take(1).toBlocking().single()); assertEquals("three", Observable.from(items).takeLast(1).toBlocking().single()); } @Test public void fromArityArgs3() { Observable<String> items = Observable.just("one", "two", "three"); assertEquals(new Integer(3), items.count().toBlocking().single()); assertEquals("two", items.skip(1).take(1).toBlocking().single()); assertEquals("three", items.takeLast(1).toBlocking().single()); } @Test public void fromArityArgs1() { Observable<String> items = Observable.just("one"); assertEquals(new Integer(1), items.count().toBlocking().single()); assertEquals("one", items.takeLast(1).toBlocking().single()); } @Test public void testCreate() { Observable<String> observable = Observable.create(new OnSubscribe<String>() { @Override public void call(Subscriber<? super String> Observer) { Observer.onNext("one"); Observer.onNext("two"); Observer.onNext("three"); Observer.onCompleted(); } }); @SuppressWarnings("unchecked") Observer<String> observer = mock(Observer.class); observable.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)).onCompleted(); } @Test public void testCountAFewItems() { Observable<String> observable = Observable.just("a", "b", "c", "d"); observable.count().subscribe(w); // we should be called only once verify(w, times(1)).onNext(anyInt()); verify(w).onNext(4); verify(w, never()).onError(any(Throwable.class)); verify(w, times(1)).onCompleted(); } @Test public void testCountZeroItems() { Observable<String> observable = Observable.empty(); observable.count().subscribe(w); // we should be called only once verify(w, times(1)).onNext(anyInt()); verify(w).onNext(0); verify(w, never()).onError(any(Throwable.class)); verify(w, times(1)).onCompleted(); } @Test public void testCountError() { Observable<String> o = Observable.create(new OnSubscribe<String>() { @Override public void call(Subscriber<? super String> obsv) { obsv.onError(new RuntimeException()); } }); o.count().subscribe(w); verify(w, never()).onNext(anyInt()); verify(w, never()).onCompleted(); verify(w, times(1)).onError(any(RuntimeException.class)); } public void testTakeFirstWithPredicateOfSome() { Observable<Integer> observable = Observable.just(1, 3, 5, 4, 6, 3); observable.takeFirst(IS_EVEN).subscribe(w); verify(w, times(1)).onNext(anyInt()); verify(w).onNext(4); verify(w, times(1)).onCompleted(); verify(w, never()).onError(any(Throwable.class)); } @Test public void testTakeFirstWithPredicateOfNoneMatchingThePredicate() { Observable<Integer> observable = Observable.just(1, 3, 5, 7, 9, 7, 5, 3, 1); observable.takeFirst(IS_EVEN).subscribe(w); verify(w, never()).onNext(anyInt()); verify(w, times(1)).onCompleted(); verify(w, never()).onError(any(Throwable.class)); } @Test public void testTakeFirstOfSome() { Observable<Integer> observable = Observable.just(1, 2, 3); observable.take(1).subscribe(w); verify(w, times(1)).onNext(anyInt()); verify(w).onNext(1); verify(w, times(1)).onCompleted(); verify(w, never()).onError(any(Throwable.class)); } @Test public void testTakeFirstOfNone() { Observable<Integer> observable = Observable.empty(); observable.take(1).subscribe(w); verify(w, never()).onNext(anyInt()); verify(w, times(1)).onCompleted(); verify(w, never()).onError(any(Throwable.class)); } @Test public void testFirstOfNone() { Observable<Integer> observable = Observable.empty(); observable.first().subscribe(w); verify(w, never()).onNext(anyInt()); verify(w, never()).onCompleted(); verify(w, times(1)).onError(isA(NoSuchElementException.class)); } @Test public void testFirstWithPredicateOfNoneMatchingThePredicate() { Observable<Integer> observable = Observable.just(1, 3, 5, 7, 9, 7, 5, 3, 1); observable.first(IS_EVEN).subscribe(w); verify(w, never()).onNext(anyInt()); verify(w, never()).onCompleted(); verify(w, times(1)).onError(isA(NoSuchElementException.class)); } @Test public void testReduce() { Observable<Integer> observable = Observable.just(1, 2, 3, 4); observable.reduce(new Func2<Integer, Integer, Integer>() { @Override public Integer call(Integer t1, Integer t2) { return t1 + t2; } }).subscribe(w); // we should be called only once verify(w, times(1)).onNext(anyInt()); verify(w).onNext(10); } /** * A reduce should fail with an NoSuchElementException if done on an empty Observable. */ @Test(expected = NoSuchElementException.class) public void testReduceWithEmptyObservable() { Observable<Integer> observable = Observable.range(1, 0); observable.reduce(new Func2<Integer, Integer, Integer>() { @Override public Integer call(Integer t1, Integer t2) { return t1 + t2; } }).toBlocking().forEach(new Action1<Integer>() { @Override public void call(Integer t1) { // do nothing ... we expect an exception instead } }); fail("Expected an exception to be thrown"); } /** * 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> observable = Observable.range(1, 0); int value = observable.reduce(1, new Func2<Integer, Integer, Integer>() { @Override public Integer call(Integer t1, Integer t2) { return t1 + t2; } }).toBlocking().last(); assertEquals(1, value); } @Test public void testReduceWithInitialValue() { Observable<Integer> observable = Observable.just(1, 2, 3, 4); observable.reduce(50, new Func2<Integer, Integer, Integer>() { @Override public Integer call(Integer t1, Integer t2) { return t1 + t2; } }).subscribe(w); // we should be called only once verify(w, times(1)).onNext(anyInt()); verify(w).onNext(60); } @Test public void testOnSubscribeFails() { @SuppressWarnings("unchecked") Observer<String> observer = mock(Observer.class); final RuntimeException re = new RuntimeException("bad impl"); Observable<String> o = Observable.create(new OnSubscribe<String>() { @Override public void call(Subscriber<? super String> t1) { throw re; } }); o.subscribe(observer); verify(observer, times(0)).onNext(anyString()); verify(observer, times(0)).onCompleted(); verify(observer, times(1)).onError(re); } @Test public void testMaterializeDematerializeChaining() { Observable<Integer> obs = Observable.just(1); Observable<Integer> chained = obs.materialize().dematerialize(); @SuppressWarnings("unchecked") Observer<Integer> observer = mock(Observer.class); chained.subscribe(observer); verify(observer, times(1)).onNext(1); verify(observer, times(1)).onCompleted(); 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) */ @Test public void testCustomObservableWithErrorInObserverAsynchronous() throws InterruptedException { final CountDownLatch latch = new CountDownLatch(1); final AtomicInteger count = new AtomicInteger(); final AtomicReference<Throwable> error = new AtomicReference<Throwable>(); Observable.create(new OnSubscribe<String>() { @Override public void call(final Subscriber<? super String> observer) { final BooleanSubscription s = new BooleanSubscription(); new Thread(new Runnable() { @Override public void run() { try { if (!s.isUnsubscribed()) { observer.onNext("1"); observer.onNext("2"); observer.onNext("three"); observer.onNext("4"); observer.onCompleted(); } } finally { latch.countDown(); } } }).start(); } }).subscribe(new Subscriber<String>() { @Override public void onCompleted() { 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(); } }); // 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>(); Observable.create(new OnSubscribe<String>() { @Override public void call(Subscriber<? super String> observer) { observer.onNext("1"); observer.onNext("2"); observer.onNext("three"); observer.onNext("4"); observer.onCompleted(); } }).subscribe(new Subscriber<String>() { @Override public void onCompleted() { 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>(); Observable.create(new OnSubscribe<String>() { @Override public void call(Subscriber<? super String> observer) { observer.onNext("1"); observer.onNext("2"); throw new NumberFormatException(); } }).subscribe(new Subscriber<String>() { @Override public void onCompleted() { 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.create(new OnSubscribe<String>() { @Override public void call(final Subscriber<? super String> observer) { count.incrementAndGet(); new Thread(new Runnable() { @Override public void run() { observer.onNext("first"); observer.onNext("last"); observer.onCompleted(); } }).start(); } }).takeLast(1).publish(); // subscribe once final CountDownLatch latch = new CountDownLatch(1); connectable.subscribe(new Action1<String>() { @Override public void call(String value) { assertEquals("last", value); latch.countDown(); } }); // subscribe twice connectable.subscribe(new Action1<String>() { @Override public void call(String ignored) { } }); Subscription subscription = connectable.connect(); assertTrue(latch.await(1000, TimeUnit.MILLISECONDS)); assertEquals(1, count.get()); subscription.unsubscribe(); } @Test public void testReplay() throws InterruptedException { final AtomicInteger counter = new AtomicInteger(); ConnectableObservable<String> o = Observable.create(new OnSubscribe<String>() { @Override public void call(final Subscriber<? super String> observer) { new Thread(new Runnable() { @Override public void run() { counter.incrementAndGet(); observer.onNext("one"); observer.onCompleted(); } }).start(); } }).replay(); // we connect immediately and it will emit the value Subscription 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 Action1<String>() { @Override public void call(String v) { assertEquals("one", v); latch.countDown(); } }); // subscribe again o.subscribe(new Action1<String>() { @Override public void call(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.unsubscribe(); } } @Test public void testCache() throws InterruptedException { final AtomicInteger counter = new AtomicInteger(); Observable<String> o = Observable.create(new OnSubscribe<String>() { @Override public void call(final Subscriber<? super String> observer) { new Thread(new Runnable() { @Override public void run() { counter.incrementAndGet(); observer.onNext("one"); observer.onCompleted(); } }).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 Action1<String>() { @Override public void call(String v) { assertEquals("one", v); latch.countDown(); } }); // subscribe again o.subscribe(new Action1<String>() { @Override public void call(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.create(new OnSubscribe<String>() { @Override public void call(final Subscriber<? super String> observer) { new Thread(new Runnable() { @Override public void run() { counter.incrementAndGet(); observer.onNext("one"); observer.onCompleted(); } }).start(); } }).cache(1); // we then expect the following 2 subscriptions to get that same value final CountDownLatch latch = new CountDownLatch(2); // subscribe once o.subscribe(new Action1<String>() { @Override public void call(String v) { assertEquals("one", v); latch.countDown(); } }); // subscribe again o.subscribe(new Action1<String>() { @Override public void call(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 public void testErrorThrownWithoutErrorHandlerSynchronous() { try { Observable.error(new RuntimeException("failure")).subscribe(new Action1<Object>() { @Override public void call(Object t1) { // won't get anything } }); 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 */ @Test public void testErrorThrownWithoutErrorHandlerAsynchronous() throws InterruptedException { final CountDownLatch latch = new CountDownLatch(1); final AtomicReference<Throwable> exception = new AtomicReference<Throwable>(); Observable.create(new OnSubscribe<String>() { @Override public void call(final Subscriber<? super String> 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(new Action1<String>() { @Override public void call(String t1) { } }); // 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).subscribe(new Subscriber<String>() { @Override public void onCompleted() { 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> observable = Observable.just(1, "abc", false, 2L).ofType(String.class); @SuppressWarnings("unchecked") Observer<Object> observer = mock(Observer.class); observable.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( org.mockito.Matchers.any(Throwable.class)); verify(observer, times(1)).onCompleted(); } @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> observable = Observable.<Object> just(l1, l2, "123").ofType(List.class); @SuppressWarnings("unchecked") Observer<Object> observer = mock(Observer.class); observable.subscribe(observer); verify(observer, times(1)).onNext(l1); verify(observer, times(1)).onNext(l2); verify(observer, never()).onNext("123"); verify(observer, never()).onError( org.mockito.Matchers.any(Throwable.class)); verify(observer, times(1)).onCompleted(); } @Test public void testContains() { Observable<Boolean> observable = Observable.just("a", "b", null).contains("b"); @SuppressWarnings("unchecked") Observer<Object> observer = mock(Observer.class); observable.subscribe(observer); verify(observer, times(1)).onNext(true); verify(observer, never()).onNext(false); verify(observer, never()).onError( org.mockito.Matchers.any(Throwable.class)); verify(observer, times(1)).onCompleted(); } @Test public void testContainsWithInexistence() { Observable<Boolean> observable = Observable.just("a", "b", null).contains("c"); @SuppressWarnings("unchecked") Observer<Object> observer = mock(Observer.class); observable.subscribe(observer); verify(observer, times(1)).onNext(false); verify(observer, never()).onNext(true); verify(observer, never()).onError( org.mockito.Matchers.any(Throwable.class)); verify(observer, times(1)).onCompleted(); } @Test public void testContainsWithNull() { Observable<Boolean> observable = Observable.just("a", "b", null).contains(null); @SuppressWarnings("unchecked") Observer<Object> observer = mock(Observer.class); observable.subscribe(observer); verify(observer, times(1)).onNext(true); verify(observer, never()).onNext(false); verify(observer, never()).onError( org.mockito.Matchers.any(Throwable.class)); verify(observer, times(1)).onCompleted(); } @Test public void testContainsWithEmptyObservable() { Observable<Boolean> observable = Observable.<String> empty().contains("a"); @SuppressWarnings("unchecked") Observer<Object> observer = mock(Observer.class); observable.subscribe(observer); verify(observer, times(1)).onNext(false); verify(observer, never()).onNext(true); verify(observer, never()).onError( org.mockito.Matchers.any(Throwable.class)); verify(observer, times(1)).onCompleted(); } @Test public void testIgnoreElements() { Observable<Integer> observable = Observable.just(1, 2, 3).ignoreElements(); @SuppressWarnings("unchecked") Observer<Integer> observer = mock(Observer.class); observable.subscribe(observer); verify(observer, never()).onNext(any(Integer.class)); verify(observer, never()).onError(any(Throwable.class)); verify(observer, times(1)).onCompleted(); } @Test public void testJustWithScheduler() { TestScheduler scheduler = new TestScheduler(); Observable<Integer> observable = Observable.from(Arrays.asList(1, 2)).subscribeOn(scheduler); @SuppressWarnings("unchecked") Observer<Integer> observer = mock(Observer.class); observable.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)).onCompleted(); inOrder.verifyNoMoreInteractions(); } @Test public void testStartWithWithScheduler() { TestScheduler scheduler = new TestScheduler(); Observable<Integer> observable = Observable.just(3, 4).startWith(Arrays.asList(1, 2)).subscribeOn(scheduler); @SuppressWarnings("unchecked") Observer<Integer> observer = mock(Observer.class); observable.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)).onCompleted(); inOrder.verifyNoMoreInteractions(); } @Test public void testRangeWithScheduler() { TestScheduler scheduler = new TestScheduler(); Observable<Integer> observable = Observable.range(3, 4, scheduler); @SuppressWarnings("unchecked") Observer<Integer> observer = mock(Observer.class); observable.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)).onCompleted(); inOrder.verifyNoMoreInteractions(); } @Test public void testCollectToList() { Observable<List<Integer>> o = Observable.just(1, 2, 3).collect(new Func0<List<Integer>>() { @Override public List<Integer> call() { return new ArrayList<Integer>(); } }, new Action2<List<Integer>, Integer>() { @Override public void call(List<Integer> list, Integer v) { list.add(v); } }); List<Integer> list = o.toBlocking().last(); assertEquals(3, list.size()); assertEquals(1, list.get(0).intValue()); assertEquals(2, list.get(1).intValue()); assertEquals(3, list.get(2).intValue()); // test multiple subscribe List<Integer> list2 = o.toBlocking().last(); assertEquals(3, list2.size()); assertEquals(1, list2.get(0).intValue()); assertEquals(2, list2.get(1).intValue()); assertEquals(3, list2.get(2).intValue()); } @Test public void testCollectToString() { String value = Observable.just(1, 2, 3).collect(new Func0<StringBuilder>() { @Override public StringBuilder call() { return new StringBuilder(); } }, new Action2<StringBuilder, Integer>() { @Override public void call(StringBuilder sb, Integer v) { if (sb.length() > 0) { sb.append("-"); } sb.append(v); } }).toBlocking().last().toString(); assertEquals("1-2-3", value); } @Test public void testMergeWith() { TestSubscriber<Integer> ts = new TestSubscriber<Integer>(); Observable.just(1).mergeWith(Observable.just(2)).subscribe(ts); ts.assertReceivedOnNext(Arrays.asList(1, 2)); } @Test public void testConcatWith() { TestSubscriber<Integer> ts = new TestSubscriber<Integer>(); Observable.just(1).concatWith(Observable.just(2)).subscribe(ts); ts.assertReceivedOnNext(Arrays.asList(1, 2)); } @Test public void testAmbWith() { TestSubscriber<Integer> ts = new TestSubscriber<Integer>(); Observable.just(1).ambWith(Observable.just(2)).subscribe(ts); ts.assertReceivedOnNext(Arrays.asList(1)); } @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() { int[] nums = {1, 2, 3}; final AtomicInteger count = new AtomicInteger(); for(final int n: nums) { Observable .just(Boolean.TRUE, Boolean.FALSE) .takeWhile(new Func1<Boolean, Boolean>() { @Override public Boolean call(Boolean value) { return value; } }) .toList() .doOnNext(new Action1<List<Boolean>>() { @Override public void call(List<Boolean> booleans) { count.incrementAndGet(); } }) .subscribe(); } assertEquals(nums.length, count.get()); } @Test public void testCompose() { TestSubscriber<String> ts = new TestSubscriber<String>(); Observable.just(1, 2, 3).compose(new Transformer<Integer, String>() { @Override public Observable<String> call(Observable<Integer> t1) { return t1.map(new Func1<Integer, String>() { @Override public String call(Integer t1) { return String.valueOf(t1); } }); } }).subscribe(ts); ts.assertTerminalEvent(); ts.assertNoErrors(); ts.assertReceivedOnNext(Arrays.asList("1", "2", "3")); } @Test public void testErrorThrownIssue1685() { Subject<Object, Object> subject = ReplaySubject.create(); Observable.error(new RuntimeException("oops")) .materialize() .delay(1, TimeUnit.SECONDS) .dematerialize() .subscribe(subject); subject.subscribe(); subject.materialize().toBlocking().first(); System.out.println("Done"); } @Test public void testEmptyIdentity() { assertEquals(Observable.empty(), Observable.empty()); } @Test public void testEmptyIsEmpty() { Observable.<Integer>empty().subscribe(w); verify(w).onCompleted(); verify(w, never()).onNext(any(Integer.class)); verify(w, never()).onError(any(Throwable.class)); } @Test // cf. https://github.com/ReactiveX/RxJava/issues/2599 public void testSubscribingSubscriberAsObserverMaintainsSubscriptionChain() { TestSubscriber<Object> subscriber = new TestSubscriber<Object>(); Subscription subscription = Observable.just("event").subscribe((Observer<Object>) subscriber); subscription.unsubscribe(); subscriber.assertUnsubscribed(); } }