@Test public void concatVeryLongObservableOfObservablesTakeHalf() { final int n = 10000; Observable<Observable<Integer>> source = Observable.create( new OnSubscribe<Observable<Integer>>() { @Override public void call(Subscriber<? super Observable<Integer>> s) { for (int i = 0; i < n; i++) { if (s.isUnsubscribed()) { return; } s.onNext(Observable.from(i)); } s.onCompleted(); } }); Observable<List<Integer>> result = Observable.concat(source).take(n / 2).toList(); Observer<List<Integer>> o = mock(Observer.class); InOrder inOrder = inOrder(o); result.subscribe(o); List<Integer> list = new ArrayList<Integer>(n); for (int i = 0; i < n / 2; i++) { list.add(i); } inOrder.verify(o).onNext(list); inOrder.verify(o).onCompleted(); verify(o, never()).onError(any(Throwable.class)); }
/** Simple concat of 2 asynchronous observables ensuring it emits in correct order. */ @SuppressWarnings("unchecked") @Test public void testSimpleAsyncConcat() { Observer<String> observer = mock(Observer.class); TestObservable<String> o1 = new TestObservable<String>("one", "two", "three"); TestObservable<String> o2 = new TestObservable<String>("four", "five", "six"); Observable.concat(Observable.create(o1), Observable.create(o2)).subscribe(observer); try { // wait for async observables to complete o1.t.join(); o2.t.join(); } catch (Throwable e) { throw new RuntimeException("failed waiting on threads"); } InOrder inOrder = inOrder(observer); inOrder.verify(observer, times(1)).onNext("one"); inOrder.verify(observer, times(1)).onNext("two"); inOrder.verify(observer, times(1)).onNext("three"); inOrder.verify(observer, times(1)).onNext("four"); inOrder.verify(observer, times(1)).onNext("five"); inOrder.verify(observer, times(1)).onNext("six"); }
@Test public void testConcatObservableOfObservables() { @SuppressWarnings("unchecked") Observer<String> observer = mock(Observer.class); final String[] o = {"1", "3", "5", "7"}; final String[] e = {"2", "4", "6"}; final Observable<String> odds = Observable.from(o); final Observable<String> even = Observable.from(e); Observable<Observable<String>> observableOfObservables = Observable.create( new Observable.OnSubscribeFunc<Observable<String>>() { @Override public Subscription onSubscribe(Observer<? super Observable<String>> observer) { // simulate what would happen in an observable observer.onNext(odds); observer.onNext(even); observer.onCompleted(); return Subscriptions.empty(); } }); Observable<String> concat = Observable.concat(observableOfObservables); concat.subscribe(observer); verify(observer, times(7)).onNext(anyString()); }
@Test public void testFlatMapTransformsException() { Observable<Integer> onNext = Observable.from(Arrays.asList(1, 2, 3)); Observable<Integer> onCompleted = Observable.from(Arrays.asList(4)); Observable<Integer> onError = Observable.from(Arrays.asList(5)); Observable<Integer> source = Observable.concat( Observable.from(Arrays.asList(10, 20, 30)), Observable.<Integer>error(new RuntimeException("Forced failure!"))); @SuppressWarnings("unchecked") Observer<Object> o = mock(Observer.class); source.flatMap(just(onNext), just(onError), just0(onCompleted)).subscribe(o); verify(o, times(3)).onNext(1); verify(o, times(3)).onNext(2); verify(o, times(3)).onNext(3); verify(o).onNext(5); verify(o).onCompleted(); verify(o, never()).onNext(4); verify(o, never()).onError(any(Throwable.class)); }
@Test public void testConcatConcurrentWithInfinity() { final TestObservable<String> w1 = new TestObservable<String>("one", "two", "three"); // This observable will send "hello" MAX_VALUE time. final TestObservable<String> w2 = new TestObservable<String>("hello", Integer.MAX_VALUE); @SuppressWarnings("unchecked") Observer<String> observer = mock(Observer.class); @SuppressWarnings("unchecked") TestObservable<Observable<String>> observableOfObservables = new TestObservable<Observable<String>>(Observable.create(w1), Observable.create(w2)); Observable<String> concatF = Observable.concat(Observable.create(observableOfObservables)); concatF.take(50).subscribe(observer); // Wait for the thread to start up. try { w1.waitForThreadDone(); w2.waitForThreadDone(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } InOrder inOrder = inOrder(observer); inOrder.verify(observer, times(1)).onNext("one"); inOrder.verify(observer, times(1)).onNext("two"); inOrder.verify(observer, times(1)).onNext("three"); inOrder.verify(observer, times(47)).onNext("hello"); verify(observer, times(1)).onCompleted(); verify(observer, never()).onError(any(Throwable.class)); }
public Observable<V> get(User user, K key) { return Observable.concat( fromMemory(user, key), fromDiskWithCaching(user, key), fromNetworkWithCaching(user, key)) .first(); }
@Test public void testConcatNonBlockingObservables() { final CountDownLatch okToContinueW1 = new CountDownLatch(1); final CountDownLatch okToContinueW2 = new CountDownLatch(1); final TestObservable<String> w1 = new TestObservable<String>(null, okToContinueW1, "one", "two", "three"); final TestObservable<String> w2 = new TestObservable<String>(null, okToContinueW2, "four", "five", "six"); @SuppressWarnings("unchecked") Observer<String> observer = mock(Observer.class); Observable<Observable<String>> observableOfObservables = Observable.create( new Observable.OnSubscribeFunc<Observable<String>>() { @Override public Subscription onSubscribe(Observer<? super Observable<String>> observer) { // simulate what would happen in an observable observer.onNext(Observable.create(w1)); observer.onNext(Observable.create(w2)); observer.onCompleted(); return Subscriptions.empty(); } }); Observable<String> concat = Observable.concat(observableOfObservables); concat.subscribe(observer); verify(observer, times(0)).onCompleted(); try { // release both threads okToContinueW1.countDown(); okToContinueW2.countDown(); // wait for both to finish w1.t.join(); w2.t.join(); } catch (InterruptedException e) { e.printStackTrace(); } InOrder inOrder = inOrder(observer); inOrder.verify(observer, times(1)).onNext("one"); inOrder.verify(observer, times(1)).onNext("two"); inOrder.verify(observer, times(1)).onNext("three"); inOrder.verify(observer, times(1)).onNext("four"); inOrder.verify(observer, times(1)).onNext("five"); inOrder.verify(observer, times(1)).onNext("six"); verify(observer, times(1)).onCompleted(); }
@Test public void testPropogateErrorInTheMiddleOfMultibyte() { Observable<byte[]> src = Observable.from(new byte[] {(byte) 0xc2}); Observable<byte[]> err = Observable.error(new IOException()); CharsetDecoder charsetDecoder = Charset.forName("UTF-8").newDecoder(); try { StringObservable.decode(Observable.concat(src, err), charsetDecoder) .toList() .toBlockingObservable() .single(); fail(); } catch (RuntimeException e) { assertEquals(MalformedInputException.class, e.getCause().getClass()); } }
@Test public void testConcat() { @SuppressWarnings("unchecked") Observer<String> observer = mock(Observer.class); final String[] o = {"1", "3", "5", "7"}; final String[] e = {"2", "4", "6"}; final Observable<String> odds = Observable.from(o); final Observable<String> even = Observable.from(e); @SuppressWarnings("unchecked") Observable<String> concat = Observable.concat(odds, even); concat.subscribe(observer); verify(observer, times(7)).onNext(anyString()); }
@Test public void testConcatWithList() { @SuppressWarnings("unchecked") Observer<String> observer = mock(Observer.class); final String[] o = {"1", "3", "5", "7"}; final String[] e = {"2", "4", "6"}; final Observable<String> odds = Observable.from(o); final Observable<String> even = Observable.from(e); final List<Observable<String>> list = new ArrayList<Observable<String>>(); list.add(odds); list.add(even); Observable<String> concat = Observable.concat(Observable.from(list)); concat.subscribe(observer); verify(observer, times(7)).onNext(anyString()); }
/** * All observables will be running in different threads so subscribe() is unblocked. * CountDownLatch is only used in order to call unsubscribe() in a predictable manner. */ @Test public void testConcatUnsubscribeConcurrent() { final CountDownLatch callOnce = new CountDownLatch(1); final CountDownLatch okToContinue = new CountDownLatch(1); final TestObservable<String> w1 = new TestObservable<String>("one", "two", "three"); final TestObservable<String> w2 = new TestObservable<String>(callOnce, okToContinue, "four", "five", "six"); @SuppressWarnings("unchecked") Observer<String> observer = mock(Observer.class); @SuppressWarnings("unchecked") TestObservable<Observable<String>> observableOfObservables = new TestObservable<Observable<String>>(Observable.create(w1), Observable.create(w2)); Observable<String> concatF = Observable.concat(Observable.create(observableOfObservables)); Subscription s1 = concatF.subscribe(observer); try { // Block main thread to allow observable "w1" to complete and observable "w2" to call onNext // exactly once. callOnce.await(); // "four" from w2 has been processed by onNext() s1.unsubscribe(); // "five" and "six" will NOT be processed by onNext() // Unblock the observable to continue. okToContinue.countDown(); w1.t.join(); w2.t.join(); } catch (Throwable e) { e.printStackTrace(); fail(e.getMessage()); } InOrder inOrder = inOrder(observer); inOrder.verify(observer, times(1)).onNext("one"); inOrder.verify(observer, times(1)).onNext("two"); inOrder.verify(observer, times(1)).onNext("three"); inOrder.verify(observer, times(1)).onNext("four"); inOrder.verify(observer, never()).onNext("five"); inOrder.verify(observer, never()).onNext("six"); verify(observer, never()).onCompleted(); verify(observer, never()).onError(any(Throwable.class)); }
@Test public void testMultipleObservers() { Observer<Object> o1 = mock(Observer.class); Observer<Object> o2 = mock(Observer.class); TestScheduler s = new TestScheduler(); Observable<Long> timer = Observable.interval(500, TimeUnit.MILLISECONDS, s).take(2); Observable<Long> o = Observable.concat(timer, timer); o.subscribe(o1); o.subscribe(o2); InOrder inOrder1 = inOrder(o1); InOrder inOrder2 = inOrder(o2); s.advanceTimeBy(500, TimeUnit.MILLISECONDS); inOrder1.verify(o1, times(1)).onNext(0L); inOrder2.verify(o2, times(1)).onNext(0L); s.advanceTimeBy(500, TimeUnit.MILLISECONDS); inOrder1.verify(o1, times(1)).onNext(1L); inOrder2.verify(o2, times(1)).onNext(1L); s.advanceTimeBy(500, TimeUnit.MILLISECONDS); inOrder1.verify(o1, times(1)).onNext(0L); inOrder2.verify(o2, times(1)).onNext(0L); s.advanceTimeBy(500, TimeUnit.MILLISECONDS); inOrder1.verify(o1, times(1)).onNext(1L); inOrder2.verify(o2, times(1)).onNext(1L); inOrder1.verify(o1, times(1)).onCompleted(); inOrder2.verify(o2, times(1)).onCompleted(); verify(o1, never()).onError(any(Throwable.class)); verify(o2, never()).onError(any(Throwable.class)); }
@SuppressWarnings("unchecked") @Test public void testBlockedObservableOfObservables() { Observer<String> observer = mock(Observer.class); final String[] o = {"1", "3", "5", "7"}; final String[] e = {"2", "4", "6"}; final Observable<String> odds = Observable.from(o); final Observable<String> even = Observable.from(e); final CountDownLatch callOnce = new CountDownLatch(1); final CountDownLatch okToContinue = new CountDownLatch(1); TestObservable<Observable<String>> observableOfObservables = new TestObservable<Observable<String>>(callOnce, okToContinue, odds, even); Observable<String> concatF = Observable.concat(Observable.create(observableOfObservables)); concatF.subscribe(observer); try { // Block main thread to allow observables to serve up o1. callOnce.await(); } catch (Throwable ex) { ex.printStackTrace(); fail(ex.getMessage()); } // The concated observable should have served up all of the odds. verify(observer, times(1)).onNext("1"); verify(observer, times(1)).onNext("3"); verify(observer, times(1)).onNext("5"); verify(observer, times(1)).onNext("7"); try { // unblock observables so it can serve up o2 and complete okToContinue.countDown(); observableOfObservables.t.join(); } catch (Throwable ex) { ex.printStackTrace(); fail(ex.getMessage()); } // The concatenated observable should now have served up all the evens. verify(observer, times(1)).onNext("2"); verify(observer, times(1)).onNext("4"); verify(observer, times(1)).onNext("6"); }
@Test public void testDelayedErrorDeliveryWhenSafeSubscriberUnsubscribes() { TestScheduler testScheduler = new TestScheduler(); Observable<Integer> source = Observable.concat(Observable.<Integer>error(new TestException()), Observable.just(1)); @SuppressWarnings("unchecked") Observer<Integer> o = mock(Observer.class); InOrder inOrder = inOrder(o); source.observeOn(testScheduler).subscribe(o); inOrder.verify(o, never()).onError(any(TestException.class)); testScheduler.advanceTimeBy(1, TimeUnit.SECONDS); inOrder.verify(o).onError(any(TestException.class)); inOrder.verify(o, never()).onNext(anyInt()); inOrder.verify(o, never()).onCompleted(); }
/** Test unsubscribing the concatenated Observable in a single thread. */ @Test public void testConcatUnsubscribe() { final CountDownLatch callOnce = new CountDownLatch(1); final CountDownLatch okToContinue = new CountDownLatch(1); final TestObservable<String> w1 = new TestObservable<String>("one", "two", "three"); final TestObservable<String> w2 = new TestObservable<String>(callOnce, okToContinue, "four", "five", "six"); @SuppressWarnings("unchecked") final Observer<String> observer = mock(Observer.class); @SuppressWarnings("unchecked") final Observable<String> concat = Observable.concat(Observable.create(w1), Observable.create(w2)); try { // Subscribe Subscription s1 = concat.subscribe(observer); // Block main thread to allow observable "w1" to complete and observable "w2" to call onNext // once. callOnce.await(); // Unsubcribe s1.unsubscribe(); // Unblock the observable to continue. okToContinue.countDown(); w1.t.join(); w2.t.join(); } catch (Throwable e) { e.printStackTrace(); fail(e.getMessage()); } InOrder inOrder = inOrder(observer); inOrder.verify(observer, times(1)).onNext("one"); inOrder.verify(observer, times(1)).onNext("two"); inOrder.verify(observer, times(1)).onNext("three"); inOrder.verify(observer, times(1)).onNext("four"); inOrder.verify(observer, never()).onNext("five"); inOrder.verify(observer, never()).onNext("six"); inOrder.verify(observer, never()).onCompleted(); }
/** Test an async Observable that emits more async Observables */ @SuppressWarnings("unchecked") @Test public void testNestedAsyncConcat() throws Throwable { Observer<String> observer = mock(Observer.class); final TestObservable<String> o1 = new TestObservable<String>("one", "two", "three"); final TestObservable<String> o2 = new TestObservable<String>("four", "five", "six"); final TestObservable<String> o3 = new TestObservable<String>("seven", "eight", "nine"); final CountDownLatch allowThird = new CountDownLatch(1); final AtomicReference<Thread> parent = new AtomicReference<Thread>(); final CountDownLatch parentHasStarted = new CountDownLatch(1); Observable<Observable<String>> observableOfObservables = Observable.create( new Observable.OnSubscribeFunc<Observable<String>>() { @Override public Subscription onSubscribe(final Observer<? super Observable<String>> observer) { final BooleanSubscription s = new BooleanSubscription(); parent.set( new Thread( new Runnable() { @Override public void run() { try { // emit first if (!s.isUnsubscribed()) { System.out.println("Emit o1"); observer.onNext(Observable.create(o1)); } // emit second if (!s.isUnsubscribed()) { System.out.println("Emit o2"); observer.onNext(Observable.create(o2)); } // wait until sometime later and emit third try { allowThird.await(); } catch (InterruptedException e) { observer.onError(e); } if (!s.isUnsubscribed()) { System.out.println("Emit o3"); observer.onNext(Observable.create(o3)); } } catch (Throwable e) { observer.onError(e); } finally { System.out.println("Done parent Observable"); observer.onCompleted(); } } })); parent.get().start(); parentHasStarted.countDown(); return s; } }); Observable.concat(observableOfObservables).subscribe(observer); // wait for parent to start parentHasStarted.await(); try { // wait for first 2 async observables to complete System.out.println("Thread1 is starting ... waiting for it to complete ..."); o1.waitForThreadDone(); System.out.println("Thread2 is starting ... waiting for it to complete ..."); o2.waitForThreadDone(); } catch (Throwable e) { throw new RuntimeException("failed waiting on threads", e); } InOrder inOrder = inOrder(observer); inOrder.verify(observer, times(1)).onNext("one"); inOrder.verify(observer, times(1)).onNext("two"); inOrder.verify(observer, times(1)).onNext("three"); inOrder.verify(observer, times(1)).onNext("four"); inOrder.verify(observer, times(1)).onNext("five"); inOrder.verify(observer, times(1)).onNext("six"); // we shouldn't have the following 3 yet inOrder.verify(observer, never()).onNext("seven"); inOrder.verify(observer, never()).onNext("eight"); inOrder.verify(observer, never()).onNext("nine"); // we should not be completed yet verify(observer, never()).onCompleted(); verify(observer, never()).onError(any(Throwable.class)); // now allow the third allowThird.countDown(); try { // wait for 3rd to complete o3.waitForThreadDone(); } catch (Throwable e) { throw new RuntimeException("failed waiting on threads", e); } inOrder.verify(observer, times(1)).onNext("seven"); inOrder.verify(observer, times(1)).onNext("eight"); inOrder.verify(observer, times(1)).onNext("nine"); inOrder.verify(observer, times(1)).onCompleted(); verify(observer, never()).onError(any(Throwable.class)); }
/** * Perform a long operation to be used in the main thread * * @param maxResults * @return */ @Override public Observable<List<Promotion>> queryLastestPromotion(int maxResults) { return Observable.concat(queryLocalPromotions(), queryServerPromotions()) .observeOn(AndroidSchedulers.mainThread()); }